2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2682 this.headerContainerEl.dom.innerHTML = html;
2691 * Card header - holder for the card header elements.
2696 * @class Roo.bootstrap.CardHeader
2697 * @extends Roo.bootstrap.Element
2698 * Bootstrap CardHeader class
2700 * Create a new Card Header - that you can embed children into
2701 * @param {Object} config The config object
2704 Roo.bootstrap.CardHeader = function(config){
2705 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2711 container_method : 'getCardHeader'
2724 * Card footer - holder for the card footer elements.
2729 * @class Roo.bootstrap.CardFooter
2730 * @extends Roo.bootstrap.Element
2731 * Bootstrap CardFooter class
2733 * Create a new Card Footer - that you can embed children into
2734 * @param {Object} config The config object
2737 Roo.bootstrap.CardFooter = function(config){
2738 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2744 container_method : 'getCardFooter'
2757 * Card header - holder for the card header elements.
2762 * @class Roo.bootstrap.CardImageTop
2763 * @extends Roo.bootstrap.Element
2764 * Bootstrap CardImageTop class
2766 * Create a new Card Image Top container
2767 * @param {Object} config The config object
2770 Roo.bootstrap.CardImageTop = function(config){
2771 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2777 container_method : 'getCardImageTop'
2795 * @class Roo.bootstrap.Img
2796 * @extends Roo.bootstrap.Component
2797 * Bootstrap Img class
2798 * @cfg {Boolean} imgResponsive false | true
2799 * @cfg {String} border rounded | circle | thumbnail
2800 * @cfg {String} src image source
2801 * @cfg {String} alt image alternative text
2802 * @cfg {String} href a tag href
2803 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804 * @cfg {String} xsUrl xs image source
2805 * @cfg {String} smUrl sm image source
2806 * @cfg {String} mdUrl md image source
2807 * @cfg {String} lgUrl lg image source
2810 * Create a new Input
2811 * @param {Object} config The config object
2814 Roo.bootstrap.Img = function(config){
2815 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2821 * The img click event for the img.
2822 * @param {Roo.EventObject} e
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2830 imgResponsive: true,
2840 getAutoCreate : function()
2842 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843 return this.createSingleImg();
2848 cls: 'roo-image-responsive-group',
2853 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2855 if(!_this[size + 'Url']){
2861 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862 html: _this.html || cfg.html,
2863 src: _this[size + 'Url']
2866 img.cls += ' roo-image-responsive-' + size;
2868 var s = ['xs', 'sm', 'md', 'lg'];
2870 s.splice(s.indexOf(size), 1);
2872 Roo.each(s, function(ss){
2873 img.cls += ' hidden-' + ss;
2876 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877 cfg.cls += ' img-' + _this.border;
2881 cfg.alt = _this.alt;
2894 a.target = _this.target;
2898 cfg.cn.push((_this.href) ? a : img);
2905 createSingleImg : function()
2909 cls: (this.imgResponsive) ? 'img-responsive' : '',
2911 src : 'about:blank' // just incase src get's set to undefined?!?
2914 cfg.html = this.html || cfg.html;
2916 cfg.src = this.src || cfg.src;
2918 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919 cfg.cls += ' img-' + this.border;
2936 a.target = this.target;
2941 return (this.href) ? a : cfg;
2944 initEvents: function()
2947 this.el.on('click', this.onClick, this);
2952 onClick : function(e)
2954 Roo.log('img onclick');
2955 this.fireEvent('click', this, e);
2958 * Sets the url of the image - used to update it
2959 * @param {String} url the url of the image
2962 setSrc : function(url)
2966 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967 this.el.dom.src = url;
2971 this.el.select('img', true).first().dom.src = url;
2987 * @class Roo.bootstrap.Link
2988 * @extends Roo.bootstrap.Component
2989 * Bootstrap Link Class
2990 * @cfg {String} alt image alternative text
2991 * @cfg {String} href a tag href
2992 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993 * @cfg {String} html the content of the link.
2994 * @cfg {String} anchor name for the anchor link
2995 * @cfg {String} fa - favicon
2997 * @cfg {Boolean} preventDefault (true | false) default false
3001 * Create a new Input
3002 * @param {Object} config The config object
3005 Roo.bootstrap.Link = function(config){
3006 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3012 * The img click event for the img.
3013 * @param {Roo.EventObject} e
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3023 preventDefault: false,
3029 getAutoCreate : function()
3031 var html = this.html || '';
3033 if (this.fa !== false) {
3034 html = '<i class="fa fa-' + this.fa + '"></i>';
3039 // anchor's do not require html/href...
3040 if (this.anchor === false) {
3042 cfg.href = this.href || '#';
3044 cfg.name = this.anchor;
3045 if (this.html !== false || this.fa !== false) {
3048 if (this.href !== false) {
3049 cfg.href = this.href;
3053 if(this.alt !== false){
3058 if(this.target !== false) {
3059 cfg.target = this.target;
3065 initEvents: function() {
3067 if(!this.href || this.preventDefault){
3068 this.el.on('click', this.onClick, this);
3072 onClick : function(e)
3074 if(this.preventDefault){
3077 //Roo.log('img onclick');
3078 this.fireEvent('click', this, e);
3091 * @class Roo.bootstrap.Header
3092 * @extends Roo.bootstrap.Component
3093 * Bootstrap Header class
3094 * @cfg {String} html content of header
3095 * @cfg {Number} level (1|2|3|4|5|6) default 1
3098 * Create a new Header
3099 * @param {Object} config The config object
3103 Roo.bootstrap.Header = function(config){
3104 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3115 getAutoCreate : function(){
3120 tag: 'h' + (1 *this.level),
3121 html: this.html || ''
3133 * Ext JS Library 1.1.1
3134 * Copyright(c) 2006-2007, Ext JS, LLC.
3136 * Originally Released Under LGPL - original licence link has changed is not relivant.
3139 * <script type="text/javascript">
3143 * @class Roo.bootstrap.MenuMgr
3144 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3147 Roo.bootstrap.MenuMgr = function(){
3148 var menus, active, groups = {}, attached = false, lastShow = new Date();
3150 // private - called when first menu is created
3153 active = new Roo.util.MixedCollection();
3154 Roo.get(document).addKeyListener(27, function(){
3155 if(active.length > 0){
3163 if(active && active.length > 0){
3164 var c = active.clone();
3174 if(active.length < 1){
3175 Roo.get(document).un("mouseup", onMouseDown);
3183 var last = active.last();
3184 lastShow = new Date();
3187 Roo.get(document).on("mouseup", onMouseDown);
3192 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193 m.parentMenu.activeChild = m;
3194 }else if(last && last.isVisible()){
3195 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3200 function onBeforeHide(m){
3202 m.activeChild.hide();
3204 if(m.autoHideTimer){
3205 clearTimeout(m.autoHideTimer);
3206 delete m.autoHideTimer;
3211 function onBeforeShow(m){
3212 var pm = m.parentMenu;
3213 if(!pm && !m.allowOtherMenus){
3215 }else if(pm && pm.activeChild && active != m){
3216 pm.activeChild.hide();
3220 // private this should really trigger on mouseup..
3221 function onMouseDown(e){
3222 Roo.log("on Mouse Up");
3224 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225 Roo.log("MenuManager hideAll");
3234 function onBeforeCheck(mi, state){
3236 var g = groups[mi.group];
3237 for(var i = 0, l = g.length; i < l; i++){
3239 g[i].setChecked(false);
3248 * Hides all menus that are currently visible
3250 hideAll : function(){
3255 register : function(menu){
3259 menus[menu.id] = menu;
3260 menu.on("beforehide", onBeforeHide);
3261 menu.on("hide", onHide);
3262 menu.on("beforeshow", onBeforeShow);
3263 menu.on("show", onShow);
3265 if(g && menu.events["checkchange"]){
3269 groups[g].push(menu);
3270 menu.on("checkchange", onCheck);
3275 * Returns a {@link Roo.menu.Menu} object
3276 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277 * be used to generate and return a new Menu instance.
3279 get : function(menu){
3280 if(typeof menu == "string"){ // menu id
3282 }else if(menu.events){ // menu instance
3285 /*else if(typeof menu.length == 'number'){ // array of menu items?
3286 return new Roo.bootstrap.Menu({items:menu});
3287 }else{ // otherwise, must be a config
3288 return new Roo.bootstrap.Menu(menu);
3295 unregister : function(menu){
3296 delete menus[menu.id];
3297 menu.un("beforehide", onBeforeHide);
3298 menu.un("hide", onHide);
3299 menu.un("beforeshow", onBeforeShow);
3300 menu.un("show", onShow);
3302 if(g && menu.events["checkchange"]){
3303 groups[g].remove(menu);
3304 menu.un("checkchange", onCheck);
3309 registerCheckable : function(menuItem){
3310 var g = menuItem.group;
3315 groups[g].push(menuItem);
3316 menuItem.on("beforecheckchange", onBeforeCheck);
3321 unregisterCheckable : function(menuItem){
3322 var g = menuItem.group;
3324 groups[g].remove(menuItem);
3325 menuItem.un("beforecheckchange", onBeforeCheck);
3337 * @class Roo.bootstrap.Menu
3338 * @extends Roo.bootstrap.Component
3339 * Bootstrap Menu class - container for MenuItems
3340 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341 * @cfg {bool} hidden if the menu should be hidden when rendered.
3342 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3343 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3347 * @param {Object} config The config object
3351 Roo.bootstrap.Menu = function(config){
3352 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353 if (this.registerMenu && this.type != 'treeview') {
3354 Roo.bootstrap.MenuMgr.register(this);
3361 * Fires before this menu is displayed (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires before this menu is hidden (return false to block)
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is displayed
3374 * @param {Roo.menu.Menu} this
3379 * Fires after this menu is hidden
3380 * @param {Roo.menu.Menu} this
3385 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386 * @param {Roo.menu.Menu} this
3387 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388 * @param {Roo.EventObject} e
3393 * Fires when the mouse is hovering over this menu
3394 * @param {Roo.menu.Menu} this
3395 * @param {Roo.EventObject} e
3396 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3401 * Fires when the mouse exits this menu
3402 * @param {Roo.menu.Menu} this
3403 * @param {Roo.EventObject} e
3404 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3409 * Fires when a menu item contained in this menu is clicked
3410 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411 * @param {Roo.EventObject} e
3415 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3422 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3425 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3427 registerMenu : true,
3429 menuItems :false, // stores the menu items..
3439 getChildContainer : function() {
3443 getAutoCreate : function(){
3445 //if (['right'].indexOf(this.align)!==-1) {
3446 // cfg.cn[1].cls += ' pull-right'
3452 cls : 'dropdown-menu' ,
3453 style : 'z-index:1000'
3457 if (this.type === 'submenu') {
3458 cfg.cls = 'submenu active';
3460 if (this.type === 'treeview') {
3461 cfg.cls = 'treeview-menu';
3466 initEvents : function() {
3468 // Roo.log("ADD event");
3469 // Roo.log(this.triggerEl.dom);
3471 this.triggerEl.on('click', this.onTriggerClick, this);
3473 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3476 if (this.triggerEl.hasClass('nav-item')) {
3477 // dropdown toggle on the 'a' in BS4?
3478 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3480 this.triggerEl.addClass('dropdown-toggle');
3483 this.el.on('touchstart' , this.onTouch, this);
3485 this.el.on('click' , this.onClick, this);
3487 this.el.on("mouseover", this.onMouseOver, this);
3488 this.el.on("mouseout", this.onMouseOut, this);
3492 findTargetItem : function(e)
3494 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3498 //Roo.log(t); Roo.log(t.id);
3500 //Roo.log(this.menuitems);
3501 return this.menuitems.get(t.id);
3503 //return this.items.get(t.menuItemId);
3509 onTouch : function(e)
3511 Roo.log("menu.onTouch");
3512 //e.stopEvent(); this make the user popdown broken
3516 onClick : function(e)
3518 Roo.log("menu.onClick");
3520 var t = this.findTargetItem(e);
3521 if(!t || t.isContainer){
3526 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3527 if(t == this.activeItem && t.shouldDeactivate(e)){
3528 this.activeItem.deactivate();
3529 delete this.activeItem;
3533 this.setActiveItem(t, true);
3541 Roo.log('pass click event');
3545 this.fireEvent("click", this, t, e);
3549 if(!t.href.length || t.href == '#'){
3550 (function() { _this.hide(); }).defer(100);
3555 onMouseOver : function(e){
3556 var t = this.findTargetItem(e);
3559 // if(t.canActivate && !t.disabled){
3560 // this.setActiveItem(t, true);
3564 this.fireEvent("mouseover", this, e, t);
3566 isVisible : function(){
3567 return !this.hidden;
3569 onMouseOut : function(e){
3570 var t = this.findTargetItem(e);
3573 // if(t == this.activeItem && t.shouldDeactivate(e)){
3574 // this.activeItem.deactivate();
3575 // delete this.activeItem;
3578 this.fireEvent("mouseout", this, e, t);
3583 * Displays this menu relative to another element
3584 * @param {String/HTMLElement/Roo.Element} element The element to align to
3585 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586 * the element (defaults to this.defaultAlign)
3587 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3589 show : function(el, pos, parentMenu)
3591 if (false === this.fireEvent("beforeshow", this)) {
3592 Roo.log("show canceled");
3595 this.parentMenu = parentMenu;
3600 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3603 * Displays this menu at a specific xy position
3604 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3607 showAt : function(xy, parentMenu, /* private: */_e){
3608 this.parentMenu = parentMenu;
3613 this.fireEvent("beforeshow", this);
3614 //xy = this.el.adjustForConstraints(xy);
3618 this.hideMenuItems();
3619 this.hidden = false;
3620 this.triggerEl.addClass('open');
3621 this.el.addClass('show');
3623 // reassign x when hitting right
3624 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3628 // reassign y when hitting bottom
3629 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3633 // but the list may align on trigger left or trigger top... should it be a properity?
3635 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3640 this.fireEvent("show", this);
3646 this.doFocus.defer(50, this);
3650 doFocus : function(){
3652 this.focusEl.focus();
3657 * Hides this menu and optionally all parent menus
3658 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3660 hide : function(deep)
3662 if (false === this.fireEvent("beforehide", this)) {
3663 Roo.log("hide canceled");
3666 this.hideMenuItems();
3667 if(this.el && this.isVisible()){
3669 if(this.activeItem){
3670 this.activeItem.deactivate();
3671 this.activeItem = null;
3673 this.triggerEl.removeClass('open');;
3674 this.el.removeClass('show');
3676 this.fireEvent("hide", this);
3678 if(deep === true && this.parentMenu){
3679 this.parentMenu.hide(true);
3683 onTriggerClick : function(e)
3685 Roo.log('trigger click');
3687 var target = e.getTarget();
3689 Roo.log(target.nodeName.toLowerCase());
3691 if(target.nodeName.toLowerCase() === 'i'){
3697 onTriggerPress : function(e)
3699 Roo.log('trigger press');
3700 //Roo.log(e.getTarget());
3701 // Roo.log(this.triggerEl.dom);
3703 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704 var pel = Roo.get(e.getTarget());
3705 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706 Roo.log('is treeview or dropdown?');
3710 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714 if (this.isVisible()) {
3719 this.show(this.triggerEl, '?', false);
3722 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3729 hideMenuItems : function()
3731 Roo.log("hide Menu Items");
3736 this.el.select('.open',true).each(function(aa) {
3738 aa.removeClass('open');
3742 addxtypeChild : function (tree, cntr) {
3743 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3745 this.menuitems.add(comp);
3757 this.getEl().dom.innerHTML = '';
3758 this.menuitems.clear();
3772 * @class Roo.bootstrap.MenuItem
3773 * @extends Roo.bootstrap.Component
3774 * Bootstrap MenuItem class
3775 * @cfg {String} html the menu label
3776 * @cfg {String} href the link
3777 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779 * @cfg {Boolean} active used on sidebars to highlight active itesm
3780 * @cfg {String} fa favicon to show on left of menu item.
3781 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785 * Create a new MenuItem
3786 * @param {Object} config The config object
3790 Roo.bootstrap.MenuItem = function(config){
3791 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3796 * The raw click event for the entire grid.
3797 * @param {Roo.bootstrap.MenuItem} this
3798 * @param {Roo.EventObject} e
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3808 preventDefault: false,
3809 isContainer : false,
3813 getAutoCreate : function(){
3815 if(this.isContainer){
3818 cls: 'dropdown-menu-item '
3828 cls : 'dropdown-item',
3833 if (this.fa !== false) {
3836 cls : 'fa fa-' + this.fa
3845 cls: 'dropdown-menu-item',
3848 if (this.parent().type == 'treeview') {
3849 cfg.cls = 'treeview-menu';
3852 cfg.cls += ' active';
3857 anc.href = this.href || cfg.cn[0].href ;
3858 ctag.html = this.html || cfg.cn[0].html ;
3862 initEvents: function()
3864 if (this.parent().type == 'treeview') {
3865 this.el.select('a').on('click', this.onClick, this);
3869 this.menu.parentType = this.xtype;
3870 this.menu.triggerEl = this.el;
3871 this.menu = this.addxtype(Roo.apply({}, this.menu));
3875 onClick : function(e)
3877 Roo.log('item on click ');
3879 if(this.preventDefault){
3882 //this.parent().hideMenuItems();
3884 this.fireEvent('click', this, e);
3903 * @class Roo.bootstrap.MenuSeparator
3904 * @extends Roo.bootstrap.Component
3905 * Bootstrap MenuSeparator class
3908 * Create a new MenuItem
3909 * @param {Object} config The config object
3913 Roo.bootstrap.MenuSeparator = function(config){
3914 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3919 getAutoCreate : function(){
3938 * @class Roo.bootstrap.Modal
3939 * @extends Roo.bootstrap.Component
3940 * Bootstrap Modal class
3941 * @cfg {String} title Title of dialog
3942 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3944 * @cfg {Boolean} specificTitle default false
3945 * @cfg {Array} buttons Array of buttons or standard button set..
3946 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947 * @cfg {Boolean} animate default true
3948 * @cfg {Boolean} allow_close default true
3949 * @cfg {Boolean} fitwindow default false
3950 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953 * @cfg {String} size (sm|lg|xl) default empty
3954 * @cfg {Number} max_width set the max width of modal
3955 * @cfg {Boolean} editableTitle can the title be edited
3960 * Create a new Modal Dialog
3961 * @param {Object} config The config object
3964 Roo.bootstrap.Modal = function(config){
3965 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3970 * The raw btnclick event for the button
3971 * @param {Roo.EventObject} e
3976 * Fire when dialog resize
3977 * @param {Roo.bootstrap.Modal} this
3978 * @param {Roo.EventObject} e
3982 * @event titlechanged
3983 * Fire when the editable title has been changed
3984 * @param {Roo.bootstrap.Modal} this
3985 * @param {Roo.EventObject} value
3987 "titlechanged" : true
3990 this.buttons = this.buttons || [];
3993 this.tmpl = Roo.factory(this.tmpl);
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4000 title : 'test dialog',
4010 specificTitle: false,
4012 buttonPosition: 'right',
4034 editableTitle : false,
4036 onRender : function(ct, position)
4038 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4041 var cfg = Roo.apply({}, this.getAutoCreate());
4044 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4046 //if (!cfg.name.length) {
4050 cfg.cls += ' ' + this.cls;
4053 cfg.style = this.style;
4055 this.el = Roo.get(document.body).createChild(cfg, position);
4057 //var type = this.el.dom.type;
4060 if(this.tabIndex !== undefined){
4061 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4064 this.dialogEl = this.el.select('.modal-dialog',true).first();
4065 this.bodyEl = this.el.select('.modal-body',true).first();
4066 this.closeEl = this.el.select('.modal-header .close', true).first();
4067 this.headerEl = this.el.select('.modal-header',true).first();
4068 this.titleEl = this.el.select('.modal-title',true).first();
4069 this.footerEl = this.el.select('.modal-footer',true).first();
4071 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4073 //this.el.addClass("x-dlg-modal");
4075 if (this.buttons.length) {
4076 Roo.each(this.buttons, function(bb) {
4077 var b = Roo.apply({}, bb);
4078 b.xns = b.xns || Roo.bootstrap;
4079 b.xtype = b.xtype || 'Button';
4080 if (typeof(b.listeners) == 'undefined') {
4081 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4084 var btn = Roo.factory(b);
4086 btn.render(this.getButtonContainer());
4090 // render the children.
4093 if(typeof(this.items) != 'undefined'){
4094 var items = this.items;
4097 for(var i =0;i < items.length;i++) {
4098 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102 this.items = nitems;
4104 // where are these used - they used to be body/close/footer
4108 //this.el.addClass([this.fieldClass, this.cls]);
4112 getAutoCreate : function()
4114 // we will default to modal-body-overflow - might need to remove or make optional later.
4116 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4117 html : this.html || ''
4122 cls : 'modal-title',
4126 if(this.specificTitle){ // WTF is this?
4131 if (this.allow_close && Roo.bootstrap.version == 3) {
4141 if (this.editableTitle) {
4143 cls: 'form-control roo-editable-title d-none',
4149 if (this.allow_close && Roo.bootstrap.version == 4) {
4159 if(this.size.length){
4160 size = 'modal-' + this.size;
4163 var footer = Roo.bootstrap.version == 3 ?
4165 cls : 'modal-footer',
4169 cls: 'btn-' + this.buttonPosition
4174 { // BS4 uses mr-auto on left buttons....
4175 cls : 'modal-footer'
4186 cls: "modal-dialog " + size,
4189 cls : "modal-content",
4192 cls : 'modal-header',
4207 modal.cls += ' fade';
4213 getChildContainer : function() {
4218 getButtonContainer : function() {
4220 return Roo.bootstrap.version == 4 ?
4221 this.el.select('.modal-footer',true).first()
4222 : this.el.select('.modal-footer div',true).first();
4225 initEvents : function()
4227 if (this.allow_close) {
4228 this.closeEl.on('click', this.hide, this);
4230 Roo.EventManager.onWindowResize(this.resize, this, true);
4231 if (this.editableTitle) {
4232 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4233 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234 this.headerEditEl.on('keyup', function(e) {
4235 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236 this.toggleHeaderInput(false)
4239 this.headerEditEl.on('blur', function(e) {
4240 this.toggleHeaderInput(false)
4249 this.maskEl.setSize(
4250 Roo.lib.Dom.getViewWidth(true),
4251 Roo.lib.Dom.getViewHeight(true)
4254 if (this.fitwindow) {
4256 this.dialogEl.setStyle( { 'max-width' : '100%' });
4258 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4264 if(this.max_width !== 0) {
4266 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4269 this.setSize(w, this.height);
4273 if(this.max_height) {
4274 this.setSize(w,Math.min(
4276 Roo.lib.Dom.getViewportHeight(true) - 60
4282 if(!this.fit_content) {
4283 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287 this.setSize(w, Math.min(
4289 this.headerEl.getHeight() +
4290 this.footerEl.getHeight() +
4291 this.getChildHeight(this.bodyEl.dom.childNodes),
4292 Roo.lib.Dom.getViewportHeight(true) - 60)
4298 setSize : function(w,h)
4309 if (!this.rendered) {
4312 this.toggleHeaderInput(false);
4313 //this.el.setStyle('display', 'block');
4314 this.el.removeClass('hideing');
4315 this.el.dom.style.display='block';
4317 Roo.get(document.body).addClass('modal-open');
4319 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4322 this.el.addClass('show');
4323 this.el.addClass('in');
4326 this.el.addClass('show');
4327 this.el.addClass('in');
4330 // not sure how we can show data in here..
4332 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4335 Roo.get(document.body).addClass("x-body-masked");
4337 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4338 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339 this.maskEl.dom.style.display = 'block';
4340 this.maskEl.addClass('show');
4345 this.fireEvent('show', this);
4347 // set zindex here - otherwise it appears to be ignored...
4348 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4351 this.items.forEach( function(e) {
4352 e.layout ? e.layout() : false;
4360 if(this.fireEvent("beforehide", this) !== false){
4362 this.maskEl.removeClass('show');
4364 this.maskEl.dom.style.display = '';
4365 Roo.get(document.body).removeClass("x-body-masked");
4366 this.el.removeClass('in');
4367 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4369 if(this.animate){ // why
4370 this.el.addClass('hideing');
4371 this.el.removeClass('show');
4373 if (!this.el.hasClass('hideing')) {
4374 return; // it's been shown again...
4377 this.el.dom.style.display='';
4379 Roo.get(document.body).removeClass('modal-open');
4380 this.el.removeClass('hideing');
4384 this.el.removeClass('show');
4385 this.el.dom.style.display='';
4386 Roo.get(document.body).removeClass('modal-open');
4389 this.fireEvent('hide', this);
4392 isVisible : function()
4395 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399 addButton : function(str, cb)
4403 var b = Roo.apply({}, { html : str } );
4404 b.xns = b.xns || Roo.bootstrap;
4405 b.xtype = b.xtype || 'Button';
4406 if (typeof(b.listeners) == 'undefined') {
4407 b.listeners = { click : cb.createDelegate(this) };
4410 var btn = Roo.factory(b);
4412 btn.render(this.getButtonContainer());
4418 setDefaultButton : function(btn)
4420 //this.el.select('.modal-footer').()
4423 resizeTo: function(w,h)
4425 this.dialogEl.setWidth(w);
4427 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4429 this.bodyEl.setHeight(h - diff);
4431 this.fireEvent('resize', this);
4434 setContentSize : function(w, h)
4438 onButtonClick: function(btn,e)
4441 this.fireEvent('btnclick', btn.name, e);
4444 * Set the title of the Dialog
4445 * @param {String} str new Title
4447 setTitle: function(str) {
4448 this.titleEl.dom.innerHTML = str;
4452 * Set the body of the Dialog
4453 * @param {String} str new Title
4455 setBody: function(str) {
4456 this.bodyEl.dom.innerHTML = str;
4459 * Set the body of the Dialog using the template
4460 * @param {Obj} data - apply this data to the template and replace the body contents.
4462 applyBody: function(obj)
4465 Roo.log("Error - using apply Body without a template");
4468 this.tmpl.overwrite(this.bodyEl, obj);
4471 getChildHeight : function(child_nodes)
4475 child_nodes.length == 0
4480 var child_height = 0;
4482 for(var i = 0; i < child_nodes.length; i++) {
4485 * for modal with tabs...
4486 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4488 var layout_childs = child_nodes[i].childNodes;
4490 for(var j = 0; j < layout_childs.length; j++) {
4492 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4494 var layout_body_childs = layout_childs[j].childNodes;
4496 for(var k = 0; k < layout_body_childs.length; k++) {
4498 if(layout_body_childs[k].classList.contains('navbar')) {
4499 child_height += layout_body_childs[k].offsetHeight;
4503 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4505 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4507 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4509 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4525 child_height += child_nodes[i].offsetHeight;
4526 // Roo.log(child_nodes[i].offsetHeight);
4529 return child_height;
4531 toggleHeaderInput : function(is_edit)
4533 if (!this.editableTitle) {
4534 return; // not editable.
4536 if (is_edit && this.is_header_editing) {
4537 return; // already editing..
4541 this.headerEditEl.dom.value = this.title;
4542 this.headerEditEl.removeClass('d-none');
4543 this.headerEditEl.dom.focus();
4544 this.titleEl.addClass('d-none');
4546 this.is_header_editing = true;
4549 // flip back to not editing.
4550 this.title = this.headerEditEl.dom.value;
4551 this.headerEditEl.addClass('d-none');
4552 this.titleEl.removeClass('d-none');
4553 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554 this.is_header_editing = false;
4555 this.fireEvent('titlechanged', this, this.title);
4564 Roo.apply(Roo.bootstrap.Modal, {
4566 * Button config that displays a single OK button
4575 * Button config that displays Yes and No buttons
4591 * Button config that displays OK and Cancel buttons
4606 * Button config that displays Yes, No and Cancel buttons
4631 * messagebox - can be used as a replace
4635 * @class Roo.MessageBox
4636 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4645 // process text value...
4649 // Show a dialog using config options:
4651 title:'Save Changes?',
4652 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653 buttons: Roo.Msg.YESNOCANCEL,
4660 Roo.bootstrap.MessageBox = function(){
4661 var dlg, opt, mask, waitTimer;
4662 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663 var buttons, activeTextEl, bwidth;
4667 var handleButton = function(button){
4669 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673 var handleHide = function(){
4675 dlg.el.removeClass(opt.cls);
4678 // Roo.TaskMgr.stop(waitTimer);
4679 // waitTimer = null;
4684 var updateButtons = function(b){
4687 buttons["ok"].hide();
4688 buttons["cancel"].hide();
4689 buttons["yes"].hide();
4690 buttons["no"].hide();
4691 dlg.footerEl.hide();
4695 dlg.footerEl.show();
4696 for(var k in buttons){
4697 if(typeof buttons[k] != "function"){
4700 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701 width += buttons[k].el.getWidth()+15;
4711 var handleEsc = function(d, k, e){
4712 if(opt && opt.closable !== false){
4722 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723 * @return {Roo.BasicDialog} The BasicDialog element
4725 getDialog : function(){
4727 dlg = new Roo.bootstrap.Modal( {
4730 //constraintoviewport:false,
4732 //collapsible : false,
4737 //buttonAlign:"center",
4738 closeClick : function(){
4739 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4742 handleButton("cancel");
4747 dlg.on("hide", handleHide);
4749 //dlg.addKeyListener(27, handleEsc);
4751 this.buttons = buttons;
4752 var bt = this.buttonText;
4753 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4758 bodyEl = dlg.bodyEl.createChild({
4760 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761 '<textarea class="roo-mb-textarea"></textarea>' +
4762 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4764 msgEl = bodyEl.dom.firstChild;
4765 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766 textboxEl.enableDisplayMode();
4767 textboxEl.addKeyListener([10,13], function(){
4768 if(dlg.isVisible() && opt && opt.buttons){
4771 }else if(opt.buttons.yes){
4772 handleButton("yes");
4776 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777 textareaEl.enableDisplayMode();
4778 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779 progressEl.enableDisplayMode();
4781 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782 var pf = progressEl.dom.firstChild;
4784 pp = Roo.get(pf.firstChild);
4785 pp.setHeight(pf.offsetHeight);
4793 * Updates the message box body text
4794 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795 * the XHTML-compliant non-breaking space character '&#160;')
4796 * @return {Roo.MessageBox} This message box
4798 updateText : function(text)
4800 if(!dlg.isVisible() && !opt.width){
4801 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4804 msgEl.innerHTML = text || ' ';
4806 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4809 Math.min(opt.width || cw , this.maxWidth),
4810 Math.max(opt.minWidth || this.minWidth, bwidth)
4813 activeTextEl.setWidth(w);
4815 if(dlg.isVisible()){
4816 dlg.fixedcenter = false;
4818 // to big, make it scroll. = But as usual stupid IE does not support
4821 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4825 bodyEl.dom.style.height = '';
4826 bodyEl.dom.style.overflowY = '';
4829 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4831 bodyEl.dom.style.overflowX = '';
4834 dlg.setContentSize(w, bodyEl.getHeight());
4835 if(dlg.isVisible()){
4836 dlg.fixedcenter = true;
4842 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4843 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846 * @return {Roo.MessageBox} This message box
4848 updateProgress : function(value, text){
4850 this.updateText(text);
4853 if (pp) { // weird bug on my firefox - for some reason this is not defined
4854 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4861 * Returns true if the message box is currently displayed
4862 * @return {Boolean} True if the message box is visible, else false
4864 isVisible : function(){
4865 return dlg && dlg.isVisible();
4869 * Hides the message box if it is displayed
4872 if(this.isVisible()){
4878 * Displays a new message box, or reinitializes an existing message box, based on the config options
4879 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880 * The following config object properties are supported:
4882 Property Type Description
4883 ---------- --------------- ------------------------------------------------------------------------------------
4884 animEl String/Element An id or Element from which the message box should animate as it opens and
4885 closes (defaults to undefined)
4886 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable Boolean False to hide the top-right close button (defaults to true). Note that
4889 progress and wait dialogs will ignore this property and always hide the
4890 close button as they can only be closed programmatically.
4891 cls String A custom CSS class to apply to the message box element
4892 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4893 displayed (defaults to 75)
4894 fn Function A callback function to execute after closing the dialog. The arguments to the
4895 function will be btn (the name of the button that was clicked, if applicable,
4896 e.g. "ok"), and text (the value of the active text field, if applicable).
4897 Progress and wait dialogs will ignore this option since they do not respond to
4898 user actions and can only be closed programmatically, so any required function
4899 should be called by the same code after it closes the dialog.
4900 icon String A CSS class that provides a background image to be used as an icon for
4901 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4903 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4904 modal Boolean False to allow user interaction with the page while the message box is
4905 displayed (defaults to true)
4906 msg String A string that will replace the existing message box body text (defaults
4907 to the XHTML-compliant non-breaking space character ' ')
4908 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4909 progress Boolean True to display a progress bar (defaults to false)
4910 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4913 title String The title text
4914 value String The string value to set into the active textbox element if displayed
4915 wait Boolean True to display a progress bar (defaults to false)
4916 width Number The width of the dialog in pixels
4923 msg: 'Please enter your address:',
4925 buttons: Roo.MessageBox.OKCANCEL,
4928 animEl: 'addAddressBtn'
4931 * @param {Object} config Configuration options
4932 * @return {Roo.MessageBox} This message box
4934 show : function(options)
4937 // this causes nightmares if you show one dialog after another
4938 // especially on callbacks..
4940 if(this.isVisible()){
4943 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4945 Roo.log("New Dialog Message:" + options.msg )
4946 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4950 var d = this.getDialog();
4952 d.setTitle(opt.title || " ");
4953 d.closeEl.setDisplayed(opt.closable !== false);
4954 activeTextEl = textboxEl;
4955 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4960 textareaEl.setHeight(typeof opt.multiline == "number" ?
4961 opt.multiline : this.defaultTextHeight);
4962 activeTextEl = textareaEl;
4971 progressEl.setDisplayed(opt.progress === true);
4973 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4975 this.updateProgress(0);
4976 activeTextEl.dom.value = opt.value || "";
4978 dlg.setDefaultButton(activeTextEl);
4980 var bs = opt.buttons;
4984 }else if(bs && bs.yes){
4985 db = buttons["yes"];
4987 dlg.setDefaultButton(db);
4989 bwidth = updateButtons(opt.buttons);
4990 this.updateText(opt.msg);
4992 d.el.addClass(opt.cls);
4994 d.proxyDrag = opt.proxyDrag === true;
4995 d.modal = opt.modal !== false;
4996 d.mask = opt.modal !== false ? mask : false;
4998 // force it to the end of the z-index stack so it gets a cursor in FF
4999 document.body.appendChild(dlg.el.dom);
5000 d.animateTarget = null;
5001 d.show(options.animEl);
5007 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5008 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009 * and closing the message box when the process is complete.
5010 * @param {String} title The title bar text
5011 * @param {String} msg The message box body text
5012 * @return {Roo.MessageBox} This message box
5014 progress : function(title, msg){
5021 minWidth: this.minProgressWidth,
5028 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029 * If a callback function is passed it will be called after the user clicks the button, and the
5030 * id of the button that was clicked will be passed as the only parameter to the callback
5031 * (could also be the top-right close button).
5032 * @param {String} title The title bar text
5033 * @param {String} msg The message box body text
5034 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035 * @param {Object} scope (optional) The scope of the callback function
5036 * @return {Roo.MessageBox} This message box
5038 alert : function(title, msg, fn, scope)
5053 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5054 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055 * You are responsible for closing the message box when the process is complete.
5056 * @param {String} msg The message box body text
5057 * @param {String} title (optional) The title bar text
5058 * @return {Roo.MessageBox} This message box
5060 wait : function(msg, title){
5071 waitTimer = Roo.TaskMgr.start({
5073 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5081 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084 * @param {String} title The title bar text
5085 * @param {String} msg The message box body text
5086 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087 * @param {Object} scope (optional) The scope of the callback function
5088 * @return {Roo.MessageBox} This message box
5090 confirm : function(title, msg, fn, scope){
5094 buttons: this.YESNO,
5103 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5105 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106 * (could also be the top-right close button) and the text that was entered will be passed as the two
5107 * parameters to the callback.
5108 * @param {String} title The title bar text
5109 * @param {String} msg The message box body text
5110 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111 * @param {Object} scope (optional) The scope of the callback function
5112 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114 * @return {Roo.MessageBox} This message box
5116 prompt : function(title, msg, fn, scope, multiline){
5120 buttons: this.OKCANCEL,
5125 multiline: multiline,
5132 * Button config that displays a single OK button
5137 * Button config that displays Yes and No buttons
5140 YESNO : {yes:true, no:true},
5142 * Button config that displays OK and Cancel buttons
5145 OKCANCEL : {ok:true, cancel:true},
5147 * Button config that displays Yes, No and Cancel buttons
5150 YESNOCANCEL : {yes:true, no:true, cancel:true},
5153 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5156 defaultTextHeight : 75,
5158 * The maximum width in pixels of the message box (defaults to 600)
5163 * The minimum width in pixels of the message box (defaults to 100)
5168 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5169 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5172 minProgressWidth : 250,
5174 * An object containing the default button text strings that can be overriden for localized language support.
5175 * Supported properties are: ok, cancel, yes and no.
5176 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5189 * Shorthand for {@link Roo.MessageBox}
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5201 * @class Roo.bootstrap.Navbar
5202 * @extends Roo.bootstrap.Component
5203 * Bootstrap Navbar class
5206 * Create a new Navbar
5207 * @param {Object} config The config object
5211 Roo.bootstrap.Navbar = function(config){
5212 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216 * @event beforetoggle
5217 * Fire before toggle the menu
5218 * @param {Roo.EventObject} e
5220 "beforetoggle" : true
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5233 getAutoCreate : function(){
5236 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240 initEvents :function ()
5242 //Roo.log(this.el.select('.navbar-toggle',true));
5243 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5250 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5252 var size = this.el.getSize();
5253 this.maskEl.setSize(size.width, size.height);
5254 this.maskEl.enableDisplayMode("block");
5263 getChildContainer : function()
5265 if (this.el && this.el.select('.collapse').getCount()) {
5266 return this.el.select('.collapse',true).first();
5281 onToggle : function()
5284 if(this.fireEvent('beforetoggle', this) === false){
5287 var ce = this.el.select('.navbar-collapse',true).first();
5289 if (!ce.hasClass('show')) {
5299 * Expand the navbar pulldown
5301 expand : function ()
5304 var ce = this.el.select('.navbar-collapse',true).first();
5305 if (ce.hasClass('collapsing')) {
5308 ce.dom.style.height = '';
5310 ce.addClass('in'); // old...
5311 ce.removeClass('collapse');
5312 ce.addClass('show');
5313 var h = ce.getHeight();
5315 ce.removeClass('show');
5316 // at this point we should be able to see it..
5317 ce.addClass('collapsing');
5319 ce.setHeight(0); // resize it ...
5320 ce.on('transitionend', function() {
5321 //Roo.log('done transition');
5322 ce.removeClass('collapsing');
5323 ce.addClass('show');
5324 ce.removeClass('collapse');
5326 ce.dom.style.height = '';
5327 }, this, { single: true} );
5329 ce.dom.scrollTop = 0;
5332 * Collapse the navbar pulldown
5334 collapse : function()
5336 var ce = this.el.select('.navbar-collapse',true).first();
5338 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339 // it's collapsed or collapsing..
5342 ce.removeClass('in'); // old...
5343 ce.setHeight(ce.getHeight());
5344 ce.removeClass('show');
5345 ce.addClass('collapsing');
5347 ce.on('transitionend', function() {
5348 ce.dom.style.height = '';
5349 ce.removeClass('collapsing');
5350 ce.addClass('collapse');
5351 }, this, { single: true} );
5371 * @class Roo.bootstrap.NavSimplebar
5372 * @extends Roo.bootstrap.Navbar
5373 * Bootstrap Sidebar class
5375 * @cfg {Boolean} inverse is inverted color
5377 * @cfg {String} type (nav | pills | tabs)
5378 * @cfg {Boolean} arrangement stacked | justified
5379 * @cfg {String} align (left | right) alignment
5381 * @cfg {Boolean} main (true|false) main nav bar? default false
5382 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5384 * @cfg {String} tag (header|footer|nav|div) default is nav
5386 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390 * Create a new Sidebar
5391 * @param {Object} config The config object
5395 Roo.bootstrap.NavSimplebar = function(config){
5396 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5415 getAutoCreate : function(){
5419 tag : this.tag || 'div',
5420 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5422 if (['light','white'].indexOf(this.weight) > -1) {
5423 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5425 cfg.cls += ' bg-' + this.weight;
5428 cfg.cls += ' navbar-inverse';
5432 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5434 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5443 cls: 'nav nav-' + this.xtype,
5449 this.type = this.type || 'nav';
5450 if (['tabs','pills'].indexOf(this.type) != -1) {
5451 cfg.cn[0].cls += ' nav-' + this.type
5455 if (this.type!=='nav') {
5456 Roo.log('nav type must be nav/tabs/pills')
5458 cfg.cn[0].cls += ' navbar-nav'
5464 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465 cfg.cn[0].cls += ' nav-' + this.arrangement;
5469 if (this.align === 'right') {
5470 cfg.cn[0].cls += ' navbar-right';
5495 * navbar-expand-md fixed-top
5499 * @class Roo.bootstrap.NavHeaderbar
5500 * @extends Roo.bootstrap.NavSimplebar
5501 * Bootstrap Sidebar class
5503 * @cfg {String} brand what is brand
5504 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505 * @cfg {String} brand_href href of the brand
5506 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5507 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5512 * Create a new Sidebar
5513 * @param {Object} config The config object
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5529 desktopCenter : false,
5532 getAutoCreate : function(){
5535 tag: this.nav || 'nav',
5536 cls: 'navbar navbar-expand-md',
5542 if (this.desktopCenter) {
5543 cn.push({cls : 'container', cn : []});
5551 cls: 'navbar-toggle navbar-toggler',
5552 'data-toggle': 'collapse',
5557 html: 'Toggle navigation'
5561 cls: 'icon-bar navbar-toggler-icon'
5574 cn.push( Roo.bootstrap.version == 4 ? btn : {
5576 cls: 'navbar-header',
5585 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5591 if (['light','white'].indexOf(this.weight) > -1) {
5592 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5594 cfg.cls += ' bg-' + this.weight;
5597 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5600 // tag can override this..
5602 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5605 if (this.brand !== '') {
5606 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5609 href: this.brand_href ? this.brand_href : '#',
5610 cls: 'navbar-brand',
5618 cfg.cls += ' main-nav';
5626 getHeaderChildContainer : function()
5628 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629 return this.el.select('.navbar-header',true).first();
5632 return this.getChildContainer();
5635 getChildContainer : function()
5638 return this.el.select('.roo-navbar-collapse',true).first();
5643 initEvents : function()
5645 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5647 if (this.autohide) {
5652 Roo.get(document).on('scroll',function(e) {
5653 var ns = Roo.get(document).getScroll().top;
5654 var os = prevScroll;
5658 ft.removeClass('slideDown');
5659 ft.addClass('slideUp');
5662 ft.removeClass('slideUp');
5663 ft.addClass('slideDown');
5684 * @class Roo.bootstrap.NavSidebar
5685 * @extends Roo.bootstrap.Navbar
5686 * Bootstrap Sidebar class
5689 * Create a new Sidebar
5690 * @param {Object} config The config object
5694 Roo.bootstrap.NavSidebar = function(config){
5695 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5700 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5702 getAutoCreate : function(){
5707 cls: 'sidebar sidebar-nav'
5729 * @class Roo.bootstrap.NavGroup
5730 * @extends Roo.bootstrap.Component
5731 * Bootstrap NavGroup class
5732 * @cfg {String} align (left|right)
5733 * @cfg {Boolean} inverse
5734 * @cfg {String} type (nav|pills|tab) default nav
5735 * @cfg {String} navId - reference Id for navbar.
5736 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5739 * Create a new nav group
5740 * @param {Object} config The config object
5743 Roo.bootstrap.NavGroup = function(config){
5744 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5747 Roo.bootstrap.NavGroup.register(this);
5751 * Fires when the active item changes
5752 * @param {Roo.bootstrap.NavGroup} this
5753 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5773 getAutoCreate : function()
5775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5781 if (Roo.bootstrap.version == 4) {
5782 if (['tabs','pills'].indexOf(this.type) != -1) {
5783 cfg.cls += ' nav-' + this.type;
5785 // trying to remove so header bar can right align top?
5786 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787 // do not use on header bar...
5788 cfg.cls += ' navbar-nav';
5793 if (['tabs','pills'].indexOf(this.type) != -1) {
5794 cfg.cls += ' nav-' + this.type
5796 if (this.type !== 'nav') {
5797 Roo.log('nav type must be nav/tabs/pills')
5799 cfg.cls += ' navbar-nav'
5803 if (this.parent() && this.parent().sidebar) {
5806 cls: 'dashboard-menu sidebar-menu'
5812 if (this.form === true) {
5815 cls: 'navbar-form form-inline'
5817 //nav navbar-right ml-md-auto
5818 if (this.align === 'right') {
5819 cfg.cls += ' navbar-right ml-md-auto';
5821 cfg.cls += ' navbar-left';
5825 if (this.align === 'right') {
5826 cfg.cls += ' navbar-right ml-md-auto';
5828 cfg.cls += ' mr-auto';
5832 cfg.cls += ' navbar-inverse';
5840 * sets the active Navigation item
5841 * @param {Roo.bootstrap.NavItem} the new current navitem
5843 setActiveItem : function(item)
5846 Roo.each(this.navItems, function(v){
5851 v.setActive(false, true);
5858 item.setActive(true, true);
5859 this.fireEvent('changed', this, item, prev);
5864 * gets the active Navigation item
5865 * @return {Roo.bootstrap.NavItem} the current navitem
5867 getActive : function()
5871 Roo.each(this.navItems, function(v){
5882 indexOfNav : function()
5886 Roo.each(this.navItems, function(v,i){
5897 * adds a Navigation item
5898 * @param {Roo.bootstrap.NavItem} the navitem to add
5900 addItem : function(cfg)
5902 if (this.form && Roo.bootstrap.version == 4) {
5905 var cn = new Roo.bootstrap.NavItem(cfg);
5907 cn.parentId = this.id;
5908 cn.onRender(this.el, null);
5912 * register a Navigation item
5913 * @param {Roo.bootstrap.NavItem} the navitem to add
5915 register : function(item)
5917 this.navItems.push( item);
5918 item.navId = this.navId;
5923 * clear all the Navigation item
5926 clearAll : function()
5929 this.el.dom.innerHTML = '';
5932 getNavItem: function(tabId)
5935 Roo.each(this.navItems, function(e) {
5936 if (e.tabId == tabId) {
5946 setActiveNext : function()
5948 var i = this.indexOfNav(this.getActive());
5949 if (i > this.navItems.length) {
5952 this.setActiveItem(this.navItems[i+1]);
5954 setActivePrev : function()
5956 var i = this.indexOfNav(this.getActive());
5960 this.setActiveItem(this.navItems[i-1]);
5962 clearWasActive : function(except) {
5963 Roo.each(this.navItems, function(e) {
5964 if (e.tabId != except.tabId && e.was_active) {
5965 e.was_active = false;
5972 getWasActive : function ()
5975 Roo.each(this.navItems, function(e) {
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5994 * register a Navigation Group
5995 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5997 register : function(navgrp)
5999 this.groups[navgrp.navId] = navgrp;
6003 * fetch a Navigation Group based on the navigation ID
6004 * @param {string} the navgroup to add
6005 * @returns {Roo.bootstrap.NavGroup} the navgroup
6007 get: function(navId) {
6008 if (typeof(this.groups[navId]) == 'undefined') {
6010 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6012 return this.groups[navId] ;
6027 * @class Roo.bootstrap.NavItem
6028 * @extends Roo.bootstrap.Component
6029 * Bootstrap Navbar.NavItem class
6030 * @cfg {String} href link to
6031 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032 * @cfg {Boolean} button_outline show and outlined button
6033 * @cfg {String} html content of button
6034 * @cfg {String} badge text inside badge
6035 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036 * @cfg {String} glyphicon DEPRICATED - use fa
6037 * @cfg {String} icon DEPRICATED - use fa
6038 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039 * @cfg {Boolean} active Is item active
6040 * @cfg {Boolean} disabled Is item disabled
6041 * @cfg {String} linkcls Link Class
6042 * @cfg {Boolean} preventDefault (true | false) default false
6043 * @cfg {String} tabId the tab that this item activates.
6044 * @cfg {String} tagtype (a|span) render as a href or span?
6045 * @cfg {Boolean} animateRef (true|false) link to element default false
6048 * Create a new Navbar Item
6049 * @param {Object} config The config object
6051 Roo.bootstrap.NavItem = function(config){
6052 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6057 * The raw click event for the entire grid.
6058 * @param {Roo.EventObject} e
6063 * Fires when the active item active state changes
6064 * @param {Roo.bootstrap.NavItem} this
6065 * @param {boolean} state the new state
6071 * Fires when scroll to element
6072 * @param {Roo.bootstrap.NavItem} this
6073 * @param {Object} options
6074 * @param {Roo.EventObject} e
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6091 preventDefault : false,
6099 button_outline : false,
6103 getAutoCreate : function(){
6110 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6113 cfg.cls += ' active' ;
6115 if (this.disabled) {
6116 cfg.cls += ' disabled';
6120 if (this.button_weight.length) {
6121 cfg.tag = this.href ? 'a' : 'button';
6122 cfg.html = this.html || '';
6123 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6125 cfg.href = this.href;
6128 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6131 // menu .. should add dropdown-menu class - so no need for carat..
6133 if (this.badge !== '') {
6135 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6140 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144 href : this.href || "#",
6145 html: this.html || ''
6148 if (this.tagtype == 'a') {
6149 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6153 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6156 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6158 if(this.glyphicon) {
6159 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6164 cfg.cn[0].html += " <span class='caret'></span>";
6168 if (this.badge !== '') {
6170 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6178 onRender : function(ct, position)
6180 // Roo.log("Call onRender: " + this.xtype);
6181 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186 this.navLink = this.el.select('.nav-link',true).first();
6191 initEvents: function()
6193 if (typeof (this.menu) != 'undefined') {
6194 this.menu.parentType = this.xtype;
6195 this.menu.triggerEl = this.el;
6196 this.menu = this.addxtype(Roo.apply({}, this.menu));
6199 this.el.on('click', this.onClick, this);
6201 //if(this.tagtype == 'span'){
6202 // this.el.select('span',true).on('click', this.onClick, this);
6205 // at this point parent should be available..
6206 this.parent().register(this);
6209 onClick : function(e)
6211 if (e.getTarget('.dropdown-menu-item')) {
6212 // did you click on a menu itemm.... - then don't trigger onclick..
6217 this.preventDefault ||
6220 Roo.log("NavItem - prevent Default?");
6224 if (this.disabled) {
6228 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229 if (tg && tg.transition) {
6230 Roo.log("waiting for the transitionend");
6236 //Roo.log("fire event clicked");
6237 if(this.fireEvent('click', this, e) === false){
6241 if(this.tagtype == 'span'){
6245 //Roo.log(this.href);
6246 var ael = this.el.select('a',true).first();
6249 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252 return; // ignore... - it's a 'hash' to another page.
6254 Roo.log("NavItem - prevent Default?");
6256 this.scrollToElement(e);
6260 var p = this.parent();
6262 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263 if (typeof(p.setActiveItem) !== 'undefined') {
6264 p.setActiveItem(this);
6268 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270 // remove the collapsed menu expand...
6271 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6275 isActive: function () {
6278 setActive : function(state, fire, is_was_active)
6280 if (this.active && !state && this.navId) {
6281 this.was_active = true;
6282 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6284 nv.clearWasActive(this);
6288 this.active = state;
6291 this.el.removeClass('active');
6292 this.navLink ? this.navLink.removeClass('active') : false;
6293 } else if (!this.el.hasClass('active')) {
6295 this.el.addClass('active');
6296 if (Roo.bootstrap.version == 4 && this.navLink ) {
6297 this.navLink.addClass('active');
6302 this.fireEvent('changed', this, state);
6305 // show a panel if it's registered and related..
6307 if (!this.navId || !this.tabId || !state || is_was_active) {
6311 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315 var pan = tg.getPanelByName(this.tabId);
6319 // if we can not flip to new panel - go back to old nav highlight..
6320 if (false == tg.showPanel(pan)) {
6321 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6323 var onav = nv.getWasActive();
6325 onav.setActive(true, false, true);
6334 // this should not be here...
6335 setDisabled : function(state)
6337 this.disabled = state;
6339 this.el.removeClass('disabled');
6340 } else if (!this.el.hasClass('disabled')) {
6341 this.el.addClass('disabled');
6347 * Fetch the element to display the tooltip on.
6348 * @return {Roo.Element} defaults to this.el
6350 tooltipEl : function()
6352 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6355 scrollToElement : function(e)
6357 var c = document.body;
6360 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6362 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363 c = document.documentElement;
6366 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6372 var o = target.calcOffsetsTo(c);
6379 this.fireEvent('scrollto', this, options, e);
6381 Roo.get(c).scrollTo('top', options.value, true);
6394 * <span> icon </span>
6395 * <span> text </span>
6396 * <span>badge </span>
6400 * @class Roo.bootstrap.NavSidebarItem
6401 * @extends Roo.bootstrap.NavItem
6402 * Bootstrap Navbar.NavSidebarItem class
6403 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404 * {Boolean} open is the menu open
6405 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407 * {String} buttonSize (sm|md|lg)the extra classes for the button
6408 * {Boolean} showArrow show arrow next to the text (default true)
6410 * Create a new Navbar Button
6411 * @param {Object} config The config object
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6419 * The raw click event for the entire grid.
6420 * @param {Roo.EventObject} e
6425 * Fires when the active item active state changes
6426 * @param {Roo.bootstrap.NavSidebarItem} this
6427 * @param {boolean} state the new state
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6437 badgeWeight : 'default',
6443 buttonWeight : 'default',
6449 getAutoCreate : function(){
6454 href : this.href || '#',
6460 if(this.buttonView){
6463 href : this.href || '#',
6464 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6477 cfg.cls += ' active';
6480 if (this.disabled) {
6481 cfg.cls += ' disabled';
6484 cfg.cls += ' open x-open';
6487 if (this.glyphicon || this.icon) {
6488 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6489 a.cn.push({ tag : 'i', cls : c }) ;
6492 if(!this.buttonView){
6495 html : this.html || ''
6502 if (this.badge !== '') {
6503 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6509 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6512 a.cls += ' dropdown-toggle treeview' ;
6518 initEvents : function()
6520 if (typeof (this.menu) != 'undefined') {
6521 this.menu.parentType = this.xtype;
6522 this.menu.triggerEl = this.el;
6523 this.menu = this.addxtype(Roo.apply({}, this.menu));
6526 this.el.on('click', this.onClick, this);
6528 if(this.badge !== ''){
6529 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6534 onClick : function(e)
6541 if(this.preventDefault){
6545 this.fireEvent('click', this, e);
6548 disable : function()
6550 this.setDisabled(true);
6555 this.setDisabled(false);
6558 setDisabled : function(state)
6560 if(this.disabled == state){
6564 this.disabled = state;
6567 this.el.addClass('disabled');
6571 this.el.removeClass('disabled');
6576 setActive : function(state)
6578 if(this.active == state){
6582 this.active = state;
6585 this.el.addClass('active');
6589 this.el.removeClass('active');
6594 isActive: function ()
6599 setBadge : function(str)
6605 this.badgeEl.dom.innerHTML = str;
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6624 * @class Roo.bootstrap.breadcrumb.Nav
6625 * @extends Roo.bootstrap.Component
6626 * Bootstrap Breadcrumb Nav Class
6628 * @children Roo.bootstrap.breadcrumb.Item
6631 * Create a new breadcrumb.Nav
6632 * @param {Object} config The config object
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6644 getAutoCreate : function()
6661 initEvents: function()
6663 this.olEl = this.el.select('ol',true).first();
6665 getChildContainer : function()
6681 * @class Roo.bootstrap.breadcrumb.Nav
6682 * @extends Roo.bootstrap.Component
6683 * Bootstrap Breadcrumb Nav Class
6685 * @children Roo.bootstrap.breadcrumb.Component
6686 * @cfg {String} html the content of the link.
6687 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688 * @cfg {Boolean} active is it active
6692 * Create a new breadcrumb.Nav
6693 * @param {Object} config The config object
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6702 * The img click event for the img.
6703 * @param {Roo.EventObject} e
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6715 getAutoCreate : function()
6720 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6722 if (this.href !== false) {
6729 cfg.html = this.html;
6735 initEvents: function()
6738 this.el.select('a', true).first().on('click',this.onClick, this)
6742 onClick : function(e)
6745 this.fireEvent('click',this, e);
6758 * @class Roo.bootstrap.Row
6759 * @extends Roo.bootstrap.Component
6760 * Bootstrap Row class (contains columns...)
6764 * @param {Object} config The config object
6767 Roo.bootstrap.Row = function(config){
6768 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6773 getAutoCreate : function(){
6792 * @class Roo.bootstrap.Pagination
6793 * @extends Roo.bootstrap.Component
6794 * Bootstrap Pagination class
6795 * @cfg {String} size xs | sm | md | lg
6796 * @cfg {Boolean} inverse false | true
6799 * Create a new Pagination
6800 * @param {Object} config The config object
6803 Roo.bootstrap.Pagination = function(config){
6804 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6813 getAutoCreate : function(){
6819 cfg.cls += ' inverse';
6825 cfg.cls += " " + this.cls;
6843 * @class Roo.bootstrap.PaginationItem
6844 * @extends Roo.bootstrap.Component
6845 * Bootstrap PaginationItem class
6846 * @cfg {String} html text
6847 * @cfg {String} href the link
6848 * @cfg {Boolean} preventDefault (true | false) default true
6849 * @cfg {Boolean} active (true | false) default false
6850 * @cfg {Boolean} disabled default false
6854 * Create a new PaginationItem
6855 * @param {Object} config The config object
6859 Roo.bootstrap.PaginationItem = function(config){
6860 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6865 * The raw click event for the entire grid.
6866 * @param {Roo.EventObject} e
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6876 preventDefault: true,
6881 getAutoCreate : function(){
6887 href : this.href ? this.href : '#',
6888 html : this.html ? this.html : ''
6898 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6908 initEvents: function() {
6910 this.el.on('click', this.onClick, this);
6913 onClick : function(e)
6915 Roo.log('PaginationItem on click ');
6916 if(this.preventDefault){
6924 this.fireEvent('click', this, e);
6940 * @class Roo.bootstrap.Slider
6941 * @extends Roo.bootstrap.Component
6942 * Bootstrap Slider class
6945 * Create a new Slider
6946 * @param {Object} config The config object
6949 Roo.bootstrap.Slider = function(config){
6950 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6955 getAutoCreate : function(){
6959 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6975 * Ext JS Library 1.1.1
6976 * Copyright(c) 2006-2007, Ext JS, LLC.
6978 * Originally Released Under LGPL - original licence link has changed is not relivant.
6981 * <script type="text/javascript">
6986 * @class Roo.grid.ColumnModel
6987 * @extends Roo.util.Observable
6988 * This is the default implementation of a ColumnModel used by the Grid. It defines
6989 * the columns in the grid.
6992 var colModel = new Roo.grid.ColumnModel([
6993 {header: "Ticker", width: 60, sortable: true, locked: true},
6994 {header: "Company Name", width: 150, sortable: true},
6995 {header: "Market Cap.", width: 100, sortable: true},
6996 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997 {header: "Employees", width: 100, sortable: true, resizable: false}
7002 * The config options listed for this class are options which may appear in each
7003 * individual column definition.
7004 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7006 * @param {Object} config An Array of column config objects. See this class's
7007 * config objects for details.
7009 Roo.grid.ColumnModel = function(config){
7011 * The config passed into the constructor
7013 this.config = config;
7016 // if no id, create one
7017 // if the column does not have a dataIndex mapping,
7018 // map it to the order it is in the config
7019 for(var i = 0, len = config.length; i < len; i++){
7021 if(typeof c.dataIndex == "undefined"){
7024 if(typeof c.renderer == "string"){
7025 c.renderer = Roo.util.Format[c.renderer];
7027 if(typeof c.id == "undefined"){
7030 if(c.editor && c.editor.xtype){
7031 c.editor = Roo.factory(c.editor, Roo.grid);
7033 if(c.editor && c.editor.isFormField){
7034 c.editor = new Roo.grid.GridEditor(c.editor);
7036 this.lookup[c.id] = c;
7040 * The width of columns which have no width specified (defaults to 100)
7043 this.defaultWidth = 100;
7046 * Default sortable of columns which have no sortable specified (defaults to false)
7049 this.defaultSortable = false;
7053 * @event widthchange
7054 * Fires when the width of a column changes.
7055 * @param {ColumnModel} this
7056 * @param {Number} columnIndex The column index
7057 * @param {Number} newWidth The new width
7059 "widthchange": true,
7061 * @event headerchange
7062 * Fires when the text of a header changes.
7063 * @param {ColumnModel} this
7064 * @param {Number} columnIndex The column index
7065 * @param {Number} newText The new header text
7067 "headerchange": true,
7069 * @event hiddenchange
7070 * Fires when a column is hidden or "unhidden".
7071 * @param {ColumnModel} this
7072 * @param {Number} columnIndex The column index
7073 * @param {Boolean} hidden true if hidden, false otherwise
7075 "hiddenchange": true,
7077 * @event columnmoved
7078 * Fires when a column is moved.
7079 * @param {ColumnModel} this
7080 * @param {Number} oldIndex
7081 * @param {Number} newIndex
7083 "columnmoved" : true,
7085 * @event columlockchange
7086 * Fires when a column's locked state is changed
7087 * @param {ColumnModel} this
7088 * @param {Number} colIndex
7089 * @param {Boolean} locked true if locked
7091 "columnlockchange" : true
7093 Roo.grid.ColumnModel.superclass.constructor.call(this);
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7097 * @cfg {String} header The header text to display in the Grid view.
7100 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102 * specified, the column's index is used as an index into the Record's data Array.
7105 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7109 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110 * Defaults to the value of the {@link #defaultSortable} property.
7111 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7114 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7117 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7120 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7123 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7126 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7132 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7135 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7138 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7141 * @cfg {String} cursor (Optional)
7144 * @cfg {String} tooltip (Optional)
7147 * @cfg {Number} xs (Optional)
7150 * @cfg {Number} sm (Optional)
7153 * @cfg {Number} md (Optional)
7156 * @cfg {Number} lg (Optional)
7159 * Returns the id of the column at the specified index.
7160 * @param {Number} index The column index
7161 * @return {String} the id
7163 getColumnId : function(index){
7164 return this.config[index].id;
7168 * Returns the column for a specified id.
7169 * @param {String} id The column id
7170 * @return {Object} the column
7172 getColumnById : function(id){
7173 return this.lookup[id];
7178 * Returns the column for a specified dataIndex.
7179 * @param {String} dataIndex The column dataIndex
7180 * @return {Object|Boolean} the column or false if not found
7182 getColumnByDataIndex: function(dataIndex){
7183 var index = this.findColumnIndex(dataIndex);
7184 return index > -1 ? this.config[index] : false;
7188 * Returns the index for a specified column id.
7189 * @param {String} id The column id
7190 * @return {Number} the index, or -1 if not found
7192 getIndexById : function(id){
7193 for(var i = 0, len = this.config.length; i < len; i++){
7194 if(this.config[i].id == id){
7202 * Returns the index for a specified column dataIndex.
7203 * @param {String} dataIndex The column dataIndex
7204 * @return {Number} the index, or -1 if not found
7207 findColumnIndex : function(dataIndex){
7208 for(var i = 0, len = this.config.length; i < len; i++){
7209 if(this.config[i].dataIndex == dataIndex){
7217 moveColumn : function(oldIndex, newIndex){
7218 var c = this.config[oldIndex];
7219 this.config.splice(oldIndex, 1);
7220 this.config.splice(newIndex, 0, c);
7221 this.dataMap = null;
7222 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7225 isLocked : function(colIndex){
7226 return this.config[colIndex].locked === true;
7229 setLocked : function(colIndex, value, suppressEvent){
7230 if(this.isLocked(colIndex) == value){
7233 this.config[colIndex].locked = value;
7235 this.fireEvent("columnlockchange", this, colIndex, value);
7239 getTotalLockedWidth : function(){
7241 for(var i = 0; i < this.config.length; i++){
7242 if(this.isLocked(i) && !this.isHidden(i)){
7243 this.totalWidth += this.getColumnWidth(i);
7249 getLockedCount : function(){
7250 for(var i = 0, len = this.config.length; i < len; i++){
7251 if(!this.isLocked(i)){
7256 return this.config.length;
7260 * Returns the number of columns.
7263 getColumnCount : function(visibleOnly){
7264 if(visibleOnly === true){
7266 for(var i = 0, len = this.config.length; i < len; i++){
7267 if(!this.isHidden(i)){
7273 return this.config.length;
7277 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278 * @param {Function} fn
7279 * @param {Object} scope (optional)
7280 * @return {Array} result
7282 getColumnsBy : function(fn, scope){
7284 for(var i = 0, len = this.config.length; i < len; i++){
7285 var c = this.config[i];
7286 if(fn.call(scope||this, c, i) === true){
7294 * Returns true if the specified column is sortable.
7295 * @param {Number} col The column index
7298 isSortable : function(col){
7299 if(typeof this.config[col].sortable == "undefined"){
7300 return this.defaultSortable;
7302 return this.config[col].sortable;
7306 * Returns the rendering (formatting) function defined for the column.
7307 * @param {Number} col The column index.
7308 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7310 getRenderer : function(col){
7311 if(!this.config[col].renderer){
7312 return Roo.grid.ColumnModel.defaultRenderer;
7314 return this.config[col].renderer;
7318 * Sets the rendering (formatting) function for a column.
7319 * @param {Number} col The column index
7320 * @param {Function} fn The function to use to process the cell's raw data
7321 * to return HTML markup for the grid view. The render function is called with
7322 * the following parameters:<ul>
7323 * <li>Data value.</li>
7324 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325 * <li>css A CSS style string to apply to the table cell.</li>
7326 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328 * <li>Row index</li>
7329 * <li>Column index</li>
7330 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7332 setRenderer : function(col, fn){
7333 this.config[col].renderer = fn;
7337 * Returns the width for the specified column.
7338 * @param {Number} col The column index
7341 getColumnWidth : function(col){
7342 return this.config[col].width * 1 || this.defaultWidth;
7346 * Sets the width for a column.
7347 * @param {Number} col The column index
7348 * @param {Number} width The new width
7350 setColumnWidth : function(col, width, suppressEvent){
7351 this.config[col].width = width;
7352 this.totalWidth = null;
7354 this.fireEvent("widthchange", this, col, width);
7359 * Returns the total width of all columns.
7360 * @param {Boolean} includeHidden True to include hidden column widths
7363 getTotalWidth : function(includeHidden){
7364 if(!this.totalWidth){
7365 this.totalWidth = 0;
7366 for(var i = 0, len = this.config.length; i < len; i++){
7367 if(includeHidden || !this.isHidden(i)){
7368 this.totalWidth += this.getColumnWidth(i);
7372 return this.totalWidth;
7376 * Returns the header for the specified column.
7377 * @param {Number} col The column index
7380 getColumnHeader : function(col){
7381 return this.config[col].header;
7385 * Sets the header for a column.
7386 * @param {Number} col The column index
7387 * @param {String} header The new header
7389 setColumnHeader : function(col, header){
7390 this.config[col].header = header;
7391 this.fireEvent("headerchange", this, col, header);
7395 * Returns the tooltip for the specified column.
7396 * @param {Number} col The column index
7399 getColumnTooltip : function(col){
7400 return this.config[col].tooltip;
7403 * Sets the tooltip for a column.
7404 * @param {Number} col The column index
7405 * @param {String} tooltip The new tooltip
7407 setColumnTooltip : function(col, tooltip){
7408 this.config[col].tooltip = tooltip;
7412 * Returns the dataIndex for the specified column.
7413 * @param {Number} col The column index
7416 getDataIndex : function(col){
7417 return this.config[col].dataIndex;
7421 * Sets the dataIndex for a column.
7422 * @param {Number} col The column index
7423 * @param {Number} dataIndex The new dataIndex
7425 setDataIndex : function(col, dataIndex){
7426 this.config[col].dataIndex = dataIndex;
7432 * Returns true if the cell is editable.
7433 * @param {Number} colIndex The column index
7434 * @param {Number} rowIndex The row index - this is nto actually used..?
7437 isCellEditable : function(colIndex, rowIndex){
7438 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442 * Returns the editor defined for the cell/column.
7443 * return false or null to disable editing.
7444 * @param {Number} colIndex The column index
7445 * @param {Number} rowIndex The row index
7448 getCellEditor : function(colIndex, rowIndex){
7449 return this.config[colIndex].editor;
7453 * Sets if a column is editable.
7454 * @param {Number} col The column index
7455 * @param {Boolean} editable True if the column is editable
7457 setEditable : function(col, editable){
7458 this.config[col].editable = editable;
7463 * Returns true if the column is hidden.
7464 * @param {Number} colIndex The column index
7467 isHidden : function(colIndex){
7468 return this.config[colIndex].hidden;
7473 * Returns true if the column width cannot be changed
7475 isFixed : function(colIndex){
7476 return this.config[colIndex].fixed;
7480 * Returns true if the column can be resized
7483 isResizable : function(colIndex){
7484 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7487 * Sets if a column is hidden.
7488 * @param {Number} colIndex The column index
7489 * @param {Boolean} hidden True if the column is hidden
7491 setHidden : function(colIndex, hidden){
7492 this.config[colIndex].hidden = hidden;
7493 this.totalWidth = null;
7494 this.fireEvent("hiddenchange", this, colIndex, hidden);
7498 * Sets the editor for a column.
7499 * @param {Number} col The column index
7500 * @param {Object} editor The editor object
7502 setEditor : function(col, editor){
7503 this.config[col].editor = editor;
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7509 if(typeof value == "object") {
7512 if(typeof value == "string" && value.length < 1){
7516 return String.format("{0}", value);
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7523 * Ext JS Library 1.1.1
7524 * Copyright(c) 2006-2007, Ext JS, LLC.
7526 * Originally Released Under LGPL - original licence link has changed is not relivant.
7529 * <script type="text/javascript">
7533 * @class Roo.LoadMask
7534 * A simple utility class for generically masking elements while loading data. If the element being masked has
7535 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7537 * element's UpdateManager load indicator and will be destroyed after the initial load.
7539 * Create a new LoadMask
7540 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541 * @param {Object} config The config object
7543 Roo.LoadMask = function(el, config){
7544 this.el = Roo.get(el);
7545 Roo.apply(this, config);
7547 this.store.on('beforeload', this.onBeforeLoad, this);
7548 this.store.on('load', this.onLoad, this);
7549 this.store.on('loadexception', this.onLoadException, this);
7550 this.removeMask = false;
7552 var um = this.el.getUpdateManager();
7553 um.showLoadIndicator = false; // disable the default indicator
7554 um.on('beforeupdate', this.onBeforeLoad, this);
7555 um.on('update', this.onLoad, this);
7556 um.on('failure', this.onLoad, this);
7557 this.removeMask = true;
7561 Roo.LoadMask.prototype = {
7563 * @cfg {Boolean} removeMask
7564 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7569 * The text to display in a centered loading message box (defaults to 'Loading...')
7573 * @cfg {String} msgCls
7574 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7576 msgCls : 'x-mask-loading',
7579 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7585 * Disables the mask to prevent it from being displayed
7587 disable : function(){
7588 this.disabled = true;
7592 * Enables the mask so that it can be displayed
7594 enable : function(){
7595 this.disabled = false;
7598 onLoadException : function()
7602 if (typeof(arguments[3]) != 'undefined') {
7603 Roo.MessageBox.alert("Error loading",arguments[3]);
7607 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7615 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7620 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624 onBeforeLoad : function(){
7626 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7631 destroy : function(){
7633 this.store.un('beforeload', this.onBeforeLoad, this);
7634 this.store.un('load', this.onLoad, this);
7635 this.store.un('loadexception', this.onLoadException, this);
7637 var um = this.el.getUpdateManager();
7638 um.un('beforeupdate', this.onBeforeLoad, this);
7639 um.un('update', this.onLoad, this);
7640 um.un('failure', this.onLoad, this);
7651 * @class Roo.bootstrap.Table
7652 * @extends Roo.bootstrap.Component
7653 * Bootstrap Table class
7654 * @cfg {String} cls table class
7655 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656 * @cfg {String} bgcolor Specifies the background color for a table
7657 * @cfg {Number} border Specifies whether the table cells should have borders or not
7658 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659 * @cfg {Number} cellspacing Specifies the space between cells
7660 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662 * @cfg {String} sortable Specifies that the table should be sortable
7663 * @cfg {String} summary Specifies a summary of the content of a table
7664 * @cfg {Number} width Specifies the width of a table
7665 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7667 * @cfg {boolean} striped Should the rows be alternative striped
7668 * @cfg {boolean} bordered Add borders to the table
7669 * @cfg {boolean} hover Add hover highlighting
7670 * @cfg {boolean} condensed Format condensed
7671 * @cfg {boolean} responsive Format condensed
7672 * @cfg {Boolean} loadMask (true|false) default false
7673 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675 * @cfg {Boolean} rowSelection (true|false) default false
7676 * @cfg {Boolean} cellSelection (true|false) default false
7677 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7679 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7680 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7684 * Create a new Table
7685 * @param {Object} config The config object
7688 Roo.bootstrap.Table = function(config){
7689 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7694 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7699 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7701 this.sm.grid = this;
7702 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703 this.sm = this.selModel;
7704 this.sm.xmodule = this.xmodule || false;
7707 if (this.cm && typeof(this.cm.config) == 'undefined') {
7708 this.colModel = new Roo.grid.ColumnModel(this.cm);
7709 this.cm = this.colModel;
7710 this.cm.xmodule = this.xmodule || false;
7713 this.store= Roo.factory(this.store, Roo.data);
7714 this.ds = this.store;
7715 this.ds.xmodule = this.xmodule || false;
7718 if (this.footer && this.store) {
7719 this.footer.dataSource = this.ds;
7720 this.footer = Roo.factory(this.footer);
7727 * Fires when a cell is clicked
7728 * @param {Roo.bootstrap.Table} this
7729 * @param {Roo.Element} el
7730 * @param {Number} rowIndex
7731 * @param {Number} columnIndex
7732 * @param {Roo.EventObject} e
7736 * @event celldblclick
7737 * Fires when a cell is double clicked
7738 * @param {Roo.bootstrap.Table} this
7739 * @param {Roo.Element} el
7740 * @param {Number} rowIndex
7741 * @param {Number} columnIndex
7742 * @param {Roo.EventObject} e
7744 "celldblclick" : true,
7747 * Fires when a row is clicked
7748 * @param {Roo.bootstrap.Table} this
7749 * @param {Roo.Element} el
7750 * @param {Number} rowIndex
7751 * @param {Roo.EventObject} e
7755 * @event rowdblclick
7756 * Fires when a row is double clicked
7757 * @param {Roo.bootstrap.Table} this
7758 * @param {Roo.Element} el
7759 * @param {Number} rowIndex
7760 * @param {Roo.EventObject} e
7762 "rowdblclick" : true,
7765 * Fires when a mouseover occur
7766 * @param {Roo.bootstrap.Table} this
7767 * @param {Roo.Element} el
7768 * @param {Number} rowIndex
7769 * @param {Number} columnIndex
7770 * @param {Roo.EventObject} e
7775 * Fires when a mouseout occur
7776 * @param {Roo.bootstrap.Table} this
7777 * @param {Roo.Element} el
7778 * @param {Number} rowIndex
7779 * @param {Number} columnIndex
7780 * @param {Roo.EventObject} e
7785 * Fires when a row is rendered, so you can change add a style to it.
7786 * @param {Roo.bootstrap.Table} this
7787 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7791 * @event rowsrendered
7792 * Fires when all the rows have been rendered
7793 * @param {Roo.bootstrap.Table} this
7795 'rowsrendered' : true,
7797 * @event contextmenu
7798 * The raw contextmenu event for the entire grid.
7799 * @param {Roo.EventObject} e
7801 "contextmenu" : true,
7803 * @event rowcontextmenu
7804 * Fires when a row is right clicked
7805 * @param {Roo.bootstrap.Table} this
7806 * @param {Number} rowIndex
7807 * @param {Roo.EventObject} e
7809 "rowcontextmenu" : true,
7811 * @event cellcontextmenu
7812 * Fires when a cell is right clicked
7813 * @param {Roo.bootstrap.Table} this
7814 * @param {Number} rowIndex
7815 * @param {Number} cellIndex
7816 * @param {Roo.EventObject} e
7818 "cellcontextmenu" : true,
7820 * @event headercontextmenu
7821 * Fires when a header is right clicked
7822 * @param {Roo.bootstrap.Table} this
7823 * @param {Number} columnIndex
7824 * @param {Roo.EventObject} e
7826 "headercontextmenu" : true
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7856 rowSelection : false,
7857 cellSelection : false,
7860 // Roo.Element - the tbody
7862 // Roo.Element - thead element
7865 container: false, // used by gridpanel...
7871 auto_hide_footer : false,
7873 getAutoCreate : function()
7875 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7882 if (this.scrollBody) {
7883 cfg.cls += ' table-body-fixed';
7886 cfg.cls += ' table-striped';
7890 cfg.cls += ' table-hover';
7892 if (this.bordered) {
7893 cfg.cls += ' table-bordered';
7895 if (this.condensed) {
7896 cfg.cls += ' table-condensed';
7898 if (this.responsive) {
7899 cfg.cls += ' table-responsive';
7903 cfg.cls+= ' ' +this.cls;
7906 // this lot should be simplifed...
7919 ].forEach(function(k) {
7927 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7930 if(this.store || this.cm){
7931 if(this.headerShow){
7932 cfg.cn.push(this.renderHeader());
7935 cfg.cn.push(this.renderBody());
7937 if(this.footerShow){
7938 cfg.cn.push(this.renderFooter());
7940 // where does this come from?
7941 //cfg.cls+= ' TableGrid';
7944 return { cn : [ cfg ] };
7947 initEvents : function()
7949 if(!this.store || !this.cm){
7952 if (this.selModel) {
7953 this.selModel.initEvents();
7957 //Roo.log('initEvents with ds!!!!');
7959 this.mainBody = this.el.select('tbody', true).first();
7960 this.mainHead = this.el.select('thead', true).first();
7961 this.mainFoot = this.el.select('tfoot', true).first();
7967 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968 e.on('click', _this.sort, _this);
7971 this.mainBody.on("click", this.onClick, this);
7972 this.mainBody.on("dblclick", this.onDblClick, this);
7974 // why is this done????? = it breaks dialogs??
7975 //this.parent().el.setStyle('position', 'relative');
7979 this.footer.parentId = this.id;
7980 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7983 this.el.select('tfoot tr td').first().addClass('hide');
7988 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7991 this.store.on('load', this.onLoad, this);
7992 this.store.on('beforeload', this.onBeforeLoad, this);
7993 this.store.on('update', this.onUpdate, this);
7994 this.store.on('add', this.onAdd, this);
7995 this.store.on("clear", this.clear, this);
7997 this.el.on("contextmenu", this.onContextMenu, this);
7999 this.mainBody.on('scroll', this.onBodyScroll, this);
8001 this.cm.on("headerchange", this.onHeaderChange, this);
8003 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007 onContextMenu : function(e, t)
8009 this.processEvent("contextmenu", e);
8012 processEvent : function(name, e)
8014 if (name != 'touchstart' ) {
8015 this.fireEvent(name, e);
8018 var t = e.getTarget();
8020 var cell = Roo.get(t);
8026 if(cell.findParent('tfoot', false, true)){
8030 if(cell.findParent('thead', false, true)){
8032 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033 cell = Roo.get(t).findParent('th', false, true);
8035 Roo.log("failed to find th in thead?");
8036 Roo.log(e.getTarget());
8041 var cellIndex = cell.dom.cellIndex;
8043 var ename = name == 'touchstart' ? 'click' : name;
8044 this.fireEvent("header" + ename, this, cellIndex, e);
8049 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050 cell = Roo.get(t).findParent('td', false, true);
8052 Roo.log("failed to find th in tbody?");
8053 Roo.log(e.getTarget());
8058 var row = cell.findParent('tr', false, true);
8059 var cellIndex = cell.dom.cellIndex;
8060 var rowIndex = row.dom.rowIndex - 1;
8064 this.fireEvent("row" + name, this, rowIndex, e);
8068 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8074 onMouseover : function(e, el)
8076 var cell = Roo.get(el);
8082 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083 cell = cell.findParent('td', false, true);
8086 var row = cell.findParent('tr', false, true);
8087 var cellIndex = cell.dom.cellIndex;
8088 var rowIndex = row.dom.rowIndex - 1; // start from 0
8090 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094 onMouseout : function(e, el)
8096 var cell = Roo.get(el);
8102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103 cell = cell.findParent('td', false, true);
8106 var row = cell.findParent('tr', false, true);
8107 var cellIndex = cell.dom.cellIndex;
8108 var rowIndex = row.dom.rowIndex - 1; // start from 0
8110 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114 onClick : function(e, el)
8116 var cell = Roo.get(el);
8118 if(!cell || (!this.cellSelection && !this.rowSelection)){
8122 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123 cell = cell.findParent('td', false, true);
8126 if(!cell || typeof(cell) == 'undefined'){
8130 var row = cell.findParent('tr', false, true);
8132 if(!row || typeof(row) == 'undefined'){
8136 var cellIndex = cell.dom.cellIndex;
8137 var rowIndex = this.getRowIndex(row);
8139 // why??? - should these not be based on SelectionModel?
8140 if(this.cellSelection){
8141 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8144 if(this.rowSelection){
8145 this.fireEvent('rowclick', this, row, rowIndex, e);
8151 onDblClick : function(e,el)
8153 var cell = Roo.get(el);
8155 if(!cell || (!this.cellSelection && !this.rowSelection)){
8159 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160 cell = cell.findParent('td', false, true);
8163 if(!cell || typeof(cell) == 'undefined'){
8167 var row = cell.findParent('tr', false, true);
8169 if(!row || typeof(row) == 'undefined'){
8173 var cellIndex = cell.dom.cellIndex;
8174 var rowIndex = this.getRowIndex(row);
8176 if(this.cellSelection){
8177 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8180 if(this.rowSelection){
8181 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185 sort : function(e,el)
8187 var col = Roo.get(el);
8189 if(!col.hasClass('sortable')){
8193 var sort = col.attr('sort');
8196 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200 this.store.sortInfo = {field : sort, direction : dir};
8203 Roo.log("calling footer first");
8204 this.footer.onClick('first');
8207 this.store.load({ params : { start : 0 } });
8211 renderHeader : function()
8219 this.totalWidth = 0;
8221 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8223 var config = cm.config[i];
8227 cls : 'x-hcol-' + i,
8229 html: cm.getColumnHeader(i)
8234 if(typeof(config.sortable) != 'undefined' && config.sortable){
8236 c.html = '<i class="glyphicon"></i>' + c.html;
8239 // could use BS4 hidden-..-down
8241 if(typeof(config.lgHeader) != 'undefined'){
8242 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8245 if(typeof(config.mdHeader) != 'undefined'){
8246 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8249 if(typeof(config.smHeader) != 'undefined'){
8250 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8253 if(typeof(config.xsHeader) != 'undefined'){
8254 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8261 if(typeof(config.tooltip) != 'undefined'){
8262 c.tooltip = config.tooltip;
8265 if(typeof(config.colspan) != 'undefined'){
8266 c.colspan = config.colspan;
8269 if(typeof(config.hidden) != 'undefined' && config.hidden){
8270 c.style += ' display:none;';
8273 if(typeof(config.dataIndex) != 'undefined'){
8274 c.sort = config.dataIndex;
8279 if(typeof(config.align) != 'undefined' && config.align.length){
8280 c.style += ' text-align:' + config.align + ';';
8283 if(typeof(config.width) != 'undefined'){
8284 c.style += ' width:' + config.width + 'px;';
8285 this.totalWidth += config.width;
8287 this.totalWidth += 100; // assume minimum of 100 per column?
8290 if(typeof(config.cls) != 'undefined'){
8291 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8294 ['xs','sm','md','lg'].map(function(size){
8296 if(typeof(config[size]) == 'undefined'){
8300 if (!config[size]) { // 0 = hidden
8301 // BS 4 '0' is treated as hide that column and below.
8302 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306 c.cls += ' col-' + size + '-' + config[size] + (
8307 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8319 renderBody : function()
8329 colspan : this.cm.getColumnCount()
8339 renderFooter : function()
8349 colspan : this.cm.getColumnCount()
8363 // Roo.log('ds onload');
8368 var ds = this.store;
8370 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372 if (_this.store.sortInfo) {
8374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375 e.select('i', true).addClass(['glyphicon-arrow-up']);
8378 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379 e.select('i', true).addClass(['glyphicon-arrow-down']);
8384 var tbody = this.mainBody;
8386 if(ds.getCount() > 0){
8387 ds.data.each(function(d,rowIndex){
8388 var row = this.renderRow(cm, ds, rowIndex);
8390 tbody.createChild(row);
8394 if(row.cellObjects.length){
8395 Roo.each(row.cellObjects, function(r){
8396 _this.renderCellObject(r);
8403 var tfoot = this.el.select('tfoot', true).first();
8405 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8407 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8409 var total = this.ds.getTotalCount();
8411 if(this.footer.pageSize < total){
8412 this.mainFoot.show();
8416 Roo.each(this.el.select('tbody td', true).elements, function(e){
8417 e.on('mouseover', _this.onMouseover, _this);
8420 Roo.each(this.el.select('tbody td', true).elements, function(e){
8421 e.on('mouseout', _this.onMouseout, _this);
8423 this.fireEvent('rowsrendered', this);
8429 onUpdate : function(ds,record)
8431 this.refreshRow(record);
8435 onRemove : function(ds, record, index, isUpdate){
8436 if(isUpdate !== true){
8437 this.fireEvent("beforerowremoved", this, index, record);
8439 var bt = this.mainBody.dom;
8441 var rows = this.el.select('tbody > tr', true).elements;
8443 if(typeof(rows[index]) != 'undefined'){
8444 bt.removeChild(rows[index].dom);
8447 // if(bt.rows[index]){
8448 // bt.removeChild(bt.rows[index]);
8451 if(isUpdate !== true){
8452 //this.stripeRows(index);
8453 //this.syncRowHeights(index, index);
8455 this.fireEvent("rowremoved", this, index, record);
8459 onAdd : function(ds, records, rowIndex)
8461 //Roo.log('on Add called');
8462 // - note this does not handle multiple adding very well..
8463 var bt = this.mainBody.dom;
8464 for (var i =0 ; i < records.length;i++) {
8465 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466 //Roo.log(records[i]);
8467 //Roo.log(this.store.getAt(rowIndex+i));
8468 this.insertRow(this.store, rowIndex + i, false);
8475 refreshRow : function(record){
8476 var ds = this.store, index;
8477 if(typeof record == 'number'){
8479 record = ds.getAt(index);
8481 index = ds.indexOf(record);
8483 return; // should not happen - but seems to
8486 this.insertRow(ds, index, true);
8488 this.onRemove(ds, record, index+1, true);
8490 //this.syncRowHeights(index, index);
8492 this.fireEvent("rowupdated", this, index, record);
8495 insertRow : function(dm, rowIndex, isUpdate){
8498 this.fireEvent("beforerowsinserted", this, rowIndex);
8500 //var s = this.getScrollState();
8501 var row = this.renderRow(this.cm, this.store, rowIndex);
8502 // insert before rowIndex..
8503 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507 if(row.cellObjects.length){
8508 Roo.each(row.cellObjects, function(r){
8509 _this.renderCellObject(r);
8514 this.fireEvent("rowsinserted", this, rowIndex);
8515 //this.syncRowHeights(firstRow, lastRow);
8516 //this.stripeRows(firstRow);
8523 getRowDom : function(rowIndex)
8525 var rows = this.el.select('tbody > tr', true).elements;
8527 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8530 // returns the object tree for a tr..
8533 renderRow : function(cm, ds, rowIndex)
8535 var d = ds.getAt(rowIndex);
8539 cls : 'x-row-' + rowIndex,
8543 var cellObjects = [];
8545 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546 var config = cm.config[i];
8548 var renderer = cm.getRenderer(i);
8552 if(typeof(renderer) !== 'undefined'){
8553 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8555 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556 // and are rendered into the cells after the row is rendered - using the id for the element.
8558 if(typeof(value) === 'object'){
8568 rowIndex : rowIndex,
8573 this.fireEvent('rowclass', this, rowcfg);
8577 cls : rowcfg.rowClass + ' x-col-' + i,
8579 html: (typeof(value) === 'object') ? '' : value
8586 if(typeof(config.colspan) != 'undefined'){
8587 td.colspan = config.colspan;
8590 if(typeof(config.hidden) != 'undefined' && config.hidden){
8591 td.style += ' display:none;';
8594 if(typeof(config.align) != 'undefined' && config.align.length){
8595 td.style += ' text-align:' + config.align + ';';
8597 if(typeof(config.valign) != 'undefined' && config.valign.length){
8598 td.style += ' vertical-align:' + config.valign + ';';
8601 if(typeof(config.width) != 'undefined'){
8602 td.style += ' width:' + config.width + 'px;';
8605 if(typeof(config.cursor) != 'undefined'){
8606 td.style += ' cursor:' + config.cursor + ';';
8609 if(typeof(config.cls) != 'undefined'){
8610 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8613 ['xs','sm','md','lg'].map(function(size){
8615 if(typeof(config[size]) == 'undefined'){
8621 if (!config[size]) { // 0 = hidden
8622 // BS 4 '0' is treated as hide that column and below.
8623 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627 td.cls += ' col-' + size + '-' + config[size] + (
8628 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8638 row.cellObjects = cellObjects;
8646 onBeforeLoad : function()
8655 this.el.select('tbody', true).first().dom.innerHTML = '';
8658 * Show or hide a row.
8659 * @param {Number} rowIndex to show or hide
8660 * @param {Boolean} state hide
8662 setRowVisibility : function(rowIndex, state)
8664 var bt = this.mainBody.dom;
8666 var rows = this.el.select('tbody > tr', true).elements;
8668 if(typeof(rows[rowIndex]) == 'undefined'){
8671 rows[rowIndex].dom.style.display = state ? '' : 'none';
8675 getSelectionModel : function(){
8677 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8679 return this.selModel;
8682 * Render the Roo.bootstrap object from renderder
8684 renderCellObject : function(r)
8688 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8690 var t = r.cfg.render(r.container);
8693 Roo.each(r.cfg.cn, function(c){
8695 container: t.getChildContainer(),
8698 _this.renderCellObject(child);
8703 getRowIndex : function(row)
8707 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8718 * Returns the grid's underlying element = used by panel.Grid
8719 * @return {Element} The element
8721 getGridEl : function(){
8725 * Forces a resize - used by panel.Grid
8726 * @return {Element} The element
8728 autoSize : function()
8730 //var ctr = Roo.get(this.container.dom.parentElement);
8731 var ctr = Roo.get(this.el.dom);
8733 var thd = this.getGridEl().select('thead',true).first();
8734 var tbd = this.getGridEl().select('tbody', true).first();
8735 var tfd = this.getGridEl().select('tfoot', true).first();
8737 var cw = ctr.getWidth();
8738 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8742 tbd.setWidth(ctr.getWidth());
8743 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744 // this needs fixing for various usage - currently only hydra job advers I think..
8746 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8748 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8751 cw = Math.max(cw, this.totalWidth);
8752 this.getGridEl().select('tbody tr',true).setWidth(cw);
8754 // resize 'expandable coloumn?
8756 return; // we doe not have a view in this design..
8759 onBodyScroll: function()
8761 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8763 this.mainHead.setStyle({
8764 'position' : 'relative',
8765 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8771 var scrollHeight = this.mainBody.dom.scrollHeight;
8773 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8775 var height = this.mainBody.getHeight();
8777 if(scrollHeight - height == scrollTop) {
8779 var total = this.ds.getTotalCount();
8781 if(this.footer.cursor + this.footer.pageSize < total){
8783 this.footer.ds.load({
8785 start : this.footer.cursor + this.footer.pageSize,
8786 limit : this.footer.pageSize
8796 onHeaderChange : function()
8798 var header = this.renderHeader();
8799 var table = this.el.select('table', true).first();
8801 this.mainHead.remove();
8802 this.mainHead = table.createChild(header, this.mainBody, false);
8805 onHiddenChange : function(colModel, colIndex, hidden)
8807 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8810 this.CSS.updateRule(thSelector, "display", "");
8811 this.CSS.updateRule(tdSelector, "display", "");
8814 this.CSS.updateRule(thSelector, "display", "none");
8815 this.CSS.updateRule(tdSelector, "display", "none");
8818 this.onHeaderChange();
8822 setColumnWidth: function(col_index, width)
8824 // width = "md-2 xs-2..."
8825 if(!this.colModel.config[col_index]) {
8829 var w = width.split(" ");
8831 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8833 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8836 for(var j = 0; j < w.length; j++) {
8842 var size_cls = w[j].split("-");
8844 if(!Number.isInteger(size_cls[1] * 1)) {
8848 if(!this.colModel.config[col_index][size_cls[0]]) {
8852 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856 h_row[0].classList.replace(
8857 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858 "col-"+size_cls[0]+"-"+size_cls[1]
8861 for(var i = 0; i < rows.length; i++) {
8863 var size_cls = w[j].split("-");
8865 if(!Number.isInteger(size_cls[1] * 1)) {
8869 if(!this.colModel.config[col_index][size_cls[0]]) {
8873 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877 rows[i].classList.replace(
8878 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879 "col-"+size_cls[0]+"-"+size_cls[1]
8883 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8898 * @class Roo.bootstrap.TableCell
8899 * @extends Roo.bootstrap.Component
8900 * Bootstrap TableCell class
8901 * @cfg {String} html cell contain text
8902 * @cfg {String} cls cell class
8903 * @cfg {String} tag cell tag (td|th) default td
8904 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905 * @cfg {String} align Aligns the content in a cell
8906 * @cfg {String} axis Categorizes cells
8907 * @cfg {String} bgcolor Specifies the background color of a cell
8908 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909 * @cfg {Number} colspan Specifies the number of columns a cell should span
8910 * @cfg {String} headers Specifies one or more header cells a cell is related to
8911 * @cfg {Number} height Sets the height of a cell
8912 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913 * @cfg {Number} rowspan Sets the number of rows a cell should span
8914 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915 * @cfg {String} valign Vertical aligns the content in a cell
8916 * @cfg {Number} width Specifies the width of a cell
8919 * Create a new TableCell
8920 * @param {Object} config The config object
8923 Roo.bootstrap.TableCell = function(config){
8924 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8947 getAutoCreate : function(){
8948 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8968 cfg.align=this.align
8974 cfg.bgcolor=this.bgcolor
8977 cfg.charoff=this.charoff
8980 cfg.colspan=this.colspan
8983 cfg.headers=this.headers
8986 cfg.height=this.height
8989 cfg.nowrap=this.nowrap
8992 cfg.rowspan=this.rowspan
8995 cfg.scope=this.scope
8998 cfg.valign=this.valign
9001 cfg.width=this.width
9020 * @class Roo.bootstrap.TableRow
9021 * @extends Roo.bootstrap.Component
9022 * Bootstrap TableRow class
9023 * @cfg {String} cls row class
9024 * @cfg {String} align Aligns the content in a table row
9025 * @cfg {String} bgcolor Specifies a background color for a table row
9026 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027 * @cfg {String} valign Vertical aligns the content in a table row
9030 * Create a new TableRow
9031 * @param {Object} config The config object
9034 Roo.bootstrap.TableRow = function(config){
9035 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9046 getAutoCreate : function(){
9047 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9057 cfg.align = this.align;
9060 cfg.bgcolor = this.bgcolor;
9063 cfg.charoff = this.charoff;
9066 cfg.valign = this.valign;
9084 * @class Roo.bootstrap.TableBody
9085 * @extends Roo.bootstrap.Component
9086 * Bootstrap TableBody class
9087 * @cfg {String} cls element class
9088 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089 * @cfg {String} align Aligns the content inside the element
9090 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9094 * Create a new TableBody
9095 * @param {Object} config The config object
9098 Roo.bootstrap.TableBody = function(config){
9099 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9110 getAutoCreate : function(){
9111 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9125 cfg.align = this.align;
9128 cfg.charoff = this.charoff;
9131 cfg.valign = this.valign;
9138 // initEvents : function()
9145 // this.store = Roo.factory(this.store, Roo.data);
9146 // this.store.on('load', this.onLoad, this);
9148 // this.store.load();
9152 // onLoad: function ()
9154 // this.fireEvent('load', this);
9164 * Ext JS Library 1.1.1
9165 * Copyright(c) 2006-2007, Ext JS, LLC.
9167 * Originally Released Under LGPL - original licence link has changed is not relivant.
9170 * <script type="text/javascript">
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9176 * @class Roo.form.Action
9177 * Internal Class used to handle form actions
9179 * @param {Roo.form.BasicForm} el The form element or its id
9180 * @param {Object} config Configuration options
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9188 this.options = options || {};
9191 * Client Validation Failed
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9196 * Server Validation Failed
9199 Roo.form.Action.SERVER_INVALID = 'server';
9201 * Connect to Server Failed
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9206 * Reading Data from Server Failed
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9211 Roo.form.Action.prototype = {
9213 failureType : undefined,
9214 response : undefined,
9218 run : function(options){
9223 success : function(response){
9228 handleResponse : function(response){
9232 // default connection failure
9233 failure : function(response){
9235 this.response = response;
9236 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237 this.form.afterAction(this, false);
9240 processResponse : function(response){
9241 this.response = response;
9242 if(!response.responseText){
9245 this.result = this.handleResponse(response);
9249 // utility functions used internally
9250 getUrl : function(appendParams){
9251 var url = this.options.url || this.form.url || this.form.el.dom.action;
9253 var p = this.getParams();
9255 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9261 getMethod : function(){
9262 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9265 getParams : function(){
9266 var bp = this.form.baseParams;
9267 var p = this.options.params;
9269 if(typeof p == "object"){
9270 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271 }else if(typeof p == 'string' && bp){
9272 p += '&' + Roo.urlEncode(bp);
9275 p = Roo.urlEncode(bp);
9280 createCallback : function(){
9282 success: this.success,
9283 failure: this.failure,
9285 timeout: (this.form.timeout*1000),
9286 upload: this.form.fileUpload ? this.success : undefined
9291 Roo.form.Action.Submit = function(form, options){
9292 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9298 haveProgress : false,
9299 uploadComplete : false,
9301 // uploadProgress indicator.
9302 uploadProgress : function()
9304 if (!this.form.progressUrl) {
9308 if (!this.haveProgress) {
9309 Roo.MessageBox.progress("Uploading", "Uploading");
9311 if (this.uploadComplete) {
9312 Roo.MessageBox.hide();
9316 this.haveProgress = true;
9318 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9320 var c = new Roo.data.Connection();
9322 url : this.form.progressUrl,
9327 success : function(req){
9328 //console.log(data);
9332 rdata = Roo.decode(req.responseText)
9334 Roo.log("Invalid data from server..");
9338 if (!rdata || !rdata.success) {
9340 Roo.MessageBox.alert(Roo.encode(rdata));
9343 var data = rdata.data;
9345 if (this.uploadComplete) {
9346 Roo.MessageBox.hide();
9351 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9355 this.uploadProgress.defer(2000,this);
9358 failure: function(data) {
9359 Roo.log('progress url failed ');
9370 // run get Values on the form, so it syncs any secondary forms.
9371 this.form.getValues();
9373 var o = this.options;
9374 var method = this.getMethod();
9375 var isPost = method == 'POST';
9376 if(o.clientValidation === false || this.form.isValid()){
9378 if (this.form.progressUrl) {
9379 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380 (new Date() * 1) + '' + Math.random());
9385 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386 form:this.form.el.dom,
9387 url:this.getUrl(!isPost),
9389 params:isPost ? this.getParams() : null,
9390 isUpload: this.form.fileUpload,
9391 formData : this.form.formData
9394 this.uploadProgress();
9396 }else if (o.clientValidation !== false){ // client validation failed
9397 this.failureType = Roo.form.Action.CLIENT_INVALID;
9398 this.form.afterAction(this, false);
9402 success : function(response)
9404 this.uploadComplete= true;
9405 if (this.haveProgress) {
9406 Roo.MessageBox.hide();
9410 var result = this.processResponse(response);
9411 if(result === true || result.success){
9412 this.form.afterAction(this, true);
9416 this.form.markInvalid(result.errors);
9417 this.failureType = Roo.form.Action.SERVER_INVALID;
9419 this.form.afterAction(this, false);
9421 failure : function(response)
9423 this.uploadComplete= true;
9424 if (this.haveProgress) {
9425 Roo.MessageBox.hide();
9428 this.response = response;
9429 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430 this.form.afterAction(this, false);
9433 handleResponse : function(response){
9434 if(this.form.errorReader){
9435 var rs = this.form.errorReader.read(response);
9438 for(var i = 0, len = rs.records.length; i < len; i++) {
9439 var r = rs.records[i];
9443 if(errors.length < 1){
9447 success : rs.success,
9453 ret = Roo.decode(response.responseText);
9457 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9467 Roo.form.Action.Load = function(form, options){
9468 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469 this.reader = this.form.reader;
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9477 Roo.Ajax.request(Roo.apply(
9478 this.createCallback(), {
9479 method:this.getMethod(),
9480 url:this.getUrl(false),
9481 params:this.getParams()
9485 success : function(response){
9487 var result = this.processResponse(response);
9488 if(result === true || !result.success || !result.data){
9489 this.failureType = Roo.form.Action.LOAD_FAILURE;
9490 this.form.afterAction(this, false);
9493 this.form.clearInvalid();
9494 this.form.setValues(result.data);
9495 this.form.afterAction(this, true);
9498 handleResponse : function(response){
9499 if(this.form.reader){
9500 var rs = this.form.reader.read(response);
9501 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9503 success : rs.success,
9507 return Roo.decode(response.responseText);
9511 Roo.form.Action.ACTION_TYPES = {
9512 'load' : Roo.form.Action.Load,
9513 'submit' : Roo.form.Action.Submit
9522 * @class Roo.bootstrap.Form
9523 * @extends Roo.bootstrap.Component
9524 * Bootstrap Form class
9525 * @cfg {String} method GET | POST (default POST)
9526 * @cfg {String} labelAlign top | left (default top)
9527 * @cfg {String} align left | right - for navbars
9528 * @cfg {Boolean} loadMask load mask when submit (default true)
9533 * @param {Object} config The config object
9537 Roo.bootstrap.Form = function(config){
9539 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9541 Roo.bootstrap.Form.popover.apply();
9545 * @event clientvalidation
9546 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547 * @param {Form} this
9548 * @param {Boolean} valid true if the form has passed client-side validation
9550 clientvalidation: true,
9552 * @event beforeaction
9553 * Fires before any action is performed. Return false to cancel the action.
9554 * @param {Form} this
9555 * @param {Action} action The action to be performed
9559 * @event actionfailed
9560 * Fires when an action fails.
9561 * @param {Form} this
9562 * @param {Action} action The action that failed
9564 actionfailed : true,
9566 * @event actioncomplete
9567 * Fires when an action is completed.
9568 * @param {Form} this
9569 * @param {Action} action The action that completed
9571 actioncomplete : true
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9578 * @cfg {String} method
9579 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9584 * The URL to use for form actions if one isn't supplied in the action options.
9587 * @cfg {Boolean} fileUpload
9588 * Set to true if this form is a file upload.
9592 * @cfg {Object} baseParams
9593 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601 * @cfg {Sting} align (left|right) for navbar forms
9606 activeAction : null,
9609 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610 * element by passing it or its id or mask the form itself by passing in true.
9613 waitMsgTarget : false,
9618 * @cfg {Boolean} errorMask (true|false) default false
9623 * @cfg {Number} maskOffset Default 100
9628 * @cfg {Boolean} maskBody
9632 getAutoCreate : function(){
9636 method : this.method || 'POST',
9637 id : this.id || Roo.id(),
9640 if (this.parent().xtype.match(/^Nav/)) {
9641 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645 if (this.labelAlign == 'left' ) {
9646 cfg.cls += ' form-horizontal';
9652 initEvents : function()
9654 this.el.on('submit', this.onSubmit, this);
9655 // this was added as random key presses on the form where triggering form submit.
9656 this.el.on('keypress', function(e) {
9657 if (e.getCharCode() != 13) {
9660 // we might need to allow it for textareas.. and some other items.
9661 // check e.getTarget().
9663 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667 Roo.log("keypress blocked");
9675 onSubmit : function(e){
9680 * Returns true if client-side validation on the form is successful.
9683 isValid : function(){
9684 var items = this.getItems();
9688 items.each(function(f){
9694 Roo.log('invalid field: ' + f.name);
9698 if(!target && f.el.isVisible(true)){
9704 if(this.errorMask && !valid){
9705 Roo.bootstrap.Form.popover.mask(this, target);
9712 * Returns true if any fields in this form have changed since their original load.
9715 isDirty : function(){
9717 var items = this.getItems();
9718 items.each(function(f){
9728 * Performs a predefined action (submit or load) or custom actions you define on this form.
9729 * @param {String} actionName The name of the action type
9730 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9731 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732 * accept other config options):
9734 Property Type Description
9735 ---------------- --------------- ----------------------------------------------------------------------------------
9736 url String The url for the action (defaults to the form's url)
9737 method String The form method to use (defaults to the form's method, or POST if not defined)
9738 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9740 validate the form on the client (defaults to false)
9742 * @return {BasicForm} this
9744 doAction : function(action, options){
9745 if(typeof action == 'string'){
9746 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9748 if(this.fireEvent('beforeaction', this, action) !== false){
9749 this.beforeAction(action);
9750 action.run.defer(100, action);
9756 beforeAction : function(action){
9757 var o = action.options;
9762 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9764 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9767 // not really supported yet.. ??
9769 //if(this.waitMsgTarget === true){
9770 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771 //}else if(this.waitMsgTarget){
9772 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9775 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9781 afterAction : function(action, success){
9782 this.activeAction = null;
9783 var o = action.options;
9788 Roo.get(document.body).unmask();
9794 //if(this.waitMsgTarget === true){
9795 // this.el.unmask();
9796 //}else if(this.waitMsgTarget){
9797 // this.waitMsgTarget.unmask();
9799 // Roo.MessageBox.updateProgress(1);
9800 // Roo.MessageBox.hide();
9807 Roo.callback(o.success, o.scope, [this, action]);
9808 this.fireEvent('actioncomplete', this, action);
9812 // failure condition..
9813 // we have a scenario where updates need confirming.
9814 // eg. if a locking scenario exists..
9815 // we look for { errors : { needs_confirm : true }} in the response.
9817 (typeof(action.result) != 'undefined') &&
9818 (typeof(action.result.errors) != 'undefined') &&
9819 (typeof(action.result.errors.needs_confirm) != 'undefined')
9822 Roo.log("not supported yet");
9825 Roo.MessageBox.confirm(
9826 "Change requires confirmation",
9827 action.result.errorMsg,
9832 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9842 Roo.callback(o.failure, o.scope, [this, action]);
9843 // show an error message if no failed handler is set..
9844 if (!this.hasListener('actionfailed')) {
9845 Roo.log("need to add dialog support");
9847 Roo.MessageBox.alert("Error",
9848 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849 action.result.errorMsg :
9850 "Saving Failed, please check your entries or try again"
9855 this.fireEvent('actionfailed', this, action);
9860 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861 * @param {String} id The value to search for
9864 findField : function(id){
9865 var items = this.getItems();
9866 var field = items.get(id);
9868 items.each(function(f){
9869 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9876 return field || null;
9879 * Mark fields in this form invalid in bulk.
9880 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881 * @return {BasicForm} this
9883 markInvalid : function(errors){
9884 if(errors instanceof Array){
9885 for(var i = 0, len = errors.length; i < len; i++){
9886 var fieldError = errors[i];
9887 var f = this.findField(fieldError.id);
9889 f.markInvalid(fieldError.msg);
9895 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896 field.markInvalid(errors[id]);
9900 //Roo.each(this.childForms || [], function (f) {
9901 // f.markInvalid(errors);
9908 * Set values for fields in this form in bulk.
9909 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910 * @return {BasicForm} this
9912 setValues : function(values){
9913 if(values instanceof Array){ // array of objects
9914 for(var i = 0, len = values.length; i < len; i++){
9916 var f = this.findField(v.id);
9918 f.setValue(v.value);
9919 if(this.trackResetOnLoad){
9920 f.originalValue = f.getValue();
9924 }else{ // object hash
9927 if(typeof values[id] != 'function' && (field = this.findField(id))){
9929 if (field.setFromData &&
9931 field.displayField &&
9932 // combos' with local stores can
9933 // be queried via setValue()
9934 // to set their value..
9935 (field.store && !field.store.isLocal)
9939 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941 field.setFromData(sd);
9943 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9945 field.setFromData(values);
9948 field.setValue(values[id]);
9952 if(this.trackResetOnLoad){
9953 field.originalValue = field.getValue();
9959 //Roo.each(this.childForms || [], function (f) {
9960 // f.setValues(values);
9967 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968 * they are returned as an array.
9969 * @param {Boolean} asString
9972 getValues : function(asString){
9973 //if (this.childForms) {
9974 // copy values from the child forms
9975 // Roo.each(this.childForms, function (f) {
9976 // this.setValues(f.getValues());
9982 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983 if(asString === true){
9986 return Roo.urlDecode(fs);
9990 * Returns the fields in this form as an object with key/value pairs.
9991 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9994 getFieldValues : function(with_hidden)
9996 var items = this.getItems();
9998 items.each(function(f){
10000 if (!f.getName()) {
10004 var v = f.getValue();
10006 if (f.inputType =='radio') {
10007 if (typeof(ret[f.getName()]) == 'undefined') {
10008 ret[f.getName()] = ''; // empty..
10011 if (!f.el.dom.checked) {
10015 v = f.el.dom.value;
10019 if(f.xtype == 'MoneyField'){
10020 ret[f.currencyName] = f.getCurrency();
10023 // not sure if this supported any more..
10024 if ((typeof(v) == 'object') && f.getRawValue) {
10025 v = f.getRawValue() ; // dates..
10027 // combo boxes where name != hiddenName...
10028 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029 ret[f.name] = f.getRawValue();
10031 ret[f.getName()] = v;
10038 * Clears all invalid messages in this form.
10039 * @return {BasicForm} this
10041 clearInvalid : function(){
10042 var items = this.getItems();
10044 items.each(function(f){
10052 * Resets this form.
10053 * @return {BasicForm} this
10055 reset : function(){
10056 var items = this.getItems();
10057 items.each(function(f){
10061 Roo.each(this.childForms || [], function (f) {
10069 getItems : function()
10071 var r=new Roo.util.MixedCollection(false, function(o){
10072 return o.id || (o.id = Roo.id());
10074 var iter = function(el) {
10081 Roo.each(el.items,function(e) {
10090 hideFields : function(items)
10092 Roo.each(items, function(i){
10094 var f = this.findField(i);
10105 showFields : function(items)
10107 Roo.each(items, function(i){
10109 var f = this.findField(i);
10122 Roo.apply(Roo.bootstrap.Form, {
10138 intervalID : false,
10144 if(this.isApplied){
10149 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10155 this.maskEl.top.enableDisplayMode("block");
10156 this.maskEl.left.enableDisplayMode("block");
10157 this.maskEl.bottom.enableDisplayMode("block");
10158 this.maskEl.right.enableDisplayMode("block");
10160 this.toolTip = new Roo.bootstrap.Tooltip({
10161 cls : 'roo-form-error-popover',
10163 'left' : ['r-l', [-2,0], 'right'],
10164 'right' : ['l-r', [2,0], 'left'],
10165 'bottom' : ['tl-bl', [0,2], 'top'],
10166 'top' : [ 'bl-tl', [0,-2], 'bottom']
10170 this.toolTip.render(Roo.get(document.body));
10172 this.toolTip.el.enableDisplayMode("block");
10174 Roo.get(document.body).on('click', function(){
10178 Roo.get(document.body).on('touchstart', function(){
10182 this.isApplied = true
10185 mask : function(form, target)
10189 this.target = target;
10191 if(!this.form.errorMask || !target.el){
10195 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10197 Roo.log(scrollable);
10199 var ot = this.target.el.calcOffsetsTo(scrollable);
10201 var scrollTo = ot[1] - this.form.maskOffset;
10203 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10205 scrollable.scrollTo('top', scrollTo);
10207 var box = this.target.el.getBox();
10209 var zIndex = Roo.bootstrap.Modal.zIndex++;
10212 this.maskEl.top.setStyle('position', 'absolute');
10213 this.maskEl.top.setStyle('z-index', zIndex);
10214 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215 this.maskEl.top.setLeft(0);
10216 this.maskEl.top.setTop(0);
10217 this.maskEl.top.show();
10219 this.maskEl.left.setStyle('position', 'absolute');
10220 this.maskEl.left.setStyle('z-index', zIndex);
10221 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222 this.maskEl.left.setLeft(0);
10223 this.maskEl.left.setTop(box.y - this.padding);
10224 this.maskEl.left.show();
10226 this.maskEl.bottom.setStyle('position', 'absolute');
10227 this.maskEl.bottom.setStyle('z-index', zIndex);
10228 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229 this.maskEl.bottom.setLeft(0);
10230 this.maskEl.bottom.setTop(box.bottom + this.padding);
10231 this.maskEl.bottom.show();
10233 this.maskEl.right.setStyle('position', 'absolute');
10234 this.maskEl.right.setStyle('z-index', zIndex);
10235 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236 this.maskEl.right.setLeft(box.right + this.padding);
10237 this.maskEl.right.setTop(box.y - this.padding);
10238 this.maskEl.right.show();
10240 this.toolTip.bindEl = this.target.el;
10242 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10244 var tip = this.target.blankText;
10246 if(this.target.getValue() !== '' ) {
10248 if (this.target.invalidText.length) {
10249 tip = this.target.invalidText;
10250 } else if (this.target.regexText.length){
10251 tip = this.target.regexText;
10255 this.toolTip.show(tip);
10257 this.intervalID = window.setInterval(function() {
10258 Roo.bootstrap.Form.popover.unmask();
10261 window.onwheel = function(){ return false;};
10263 (function(){ this.isMasked = true; }).defer(500, this);
10267 unmask : function()
10269 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273 this.maskEl.top.setStyle('position', 'absolute');
10274 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275 this.maskEl.top.hide();
10277 this.maskEl.left.setStyle('position', 'absolute');
10278 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279 this.maskEl.left.hide();
10281 this.maskEl.bottom.setStyle('position', 'absolute');
10282 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283 this.maskEl.bottom.hide();
10285 this.maskEl.right.setStyle('position', 'absolute');
10286 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287 this.maskEl.right.hide();
10289 this.toolTip.hide();
10291 this.toolTip.el.hide();
10293 window.onwheel = function(){ return true;};
10295 if(this.intervalID){
10296 window.clearInterval(this.intervalID);
10297 this.intervalID = false;
10300 this.isMasked = false;
10310 * Ext JS Library 1.1.1
10311 * Copyright(c) 2006-2007, Ext JS, LLC.
10313 * Originally Released Under LGPL - original licence link has changed is not relivant.
10316 * <script type="text/javascript">
10319 * @class Roo.form.VTypes
10320 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10323 Roo.form.VTypes = function(){
10324 // closure these in so they are only created once.
10325 var alpha = /^[a-zA-Z_]+$/;
10326 var alphanum = /^[a-zA-Z0-9_]+$/;
10327 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10330 // All these messages and functions are configurable
10333 * The function used to validate email addresses
10334 * @param {String} value The email address
10336 'email' : function(v){
10337 return email.test(v);
10340 * The error text to display when the email validation function returns false
10343 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10345 * The keystroke filter mask to be applied on email input
10348 'emailMask' : /[a-z0-9_\.\-@]/i,
10351 * The function used to validate URLs
10352 * @param {String} value The URL
10354 'url' : function(v){
10355 return url.test(v);
10358 * The error text to display when the url validation function returns false
10361 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10364 * The function used to validate alpha values
10365 * @param {String} value The value
10367 'alpha' : function(v){
10368 return alpha.test(v);
10371 * The error text to display when the alpha validation function returns false
10374 'alphaText' : 'This field should only contain letters and _',
10376 * The keystroke filter mask to be applied on alpha input
10379 'alphaMask' : /[a-z_]/i,
10382 * The function used to validate alphanumeric values
10383 * @param {String} value The value
10385 'alphanum' : function(v){
10386 return alphanum.test(v);
10389 * The error text to display when the alphanumeric validation function returns false
10392 'alphanumText' : 'This field should only contain letters, numbers and _',
10394 * The keystroke filter mask to be applied on alphanumeric input
10397 'alphanumMask' : /[a-z0-9_]/i
10407 * @class Roo.bootstrap.Input
10408 * @extends Roo.bootstrap.Component
10409 * Bootstrap Input class
10410 * @cfg {Boolean} disabled is it disabled
10411 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10412 * @cfg {String} name name of the input
10413 * @cfg {string} fieldLabel - the label associated
10414 * @cfg {string} placeholder - placeholder to put in text.
10415 * @cfg {string} before - input group add on before
10416 * @cfg {string} after - input group add on after
10417 * @cfg {string} size - (lg|sm) or leave empty..
10418 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420 * @cfg {Number} md colspan out of 12 for computer-sized screens
10421 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422 * @cfg {string} value default value of the input
10423 * @cfg {Number} labelWidth set the width of label
10424 * @cfg {Number} labellg set the width of label (1-12)
10425 * @cfg {Number} labelmd set the width of label (1-12)
10426 * @cfg {Number} labelsm set the width of label (1-12)
10427 * @cfg {Number} labelxs set the width of label (1-12)
10428 * @cfg {String} labelAlign (top|left)
10429 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431 * @cfg {String} indicatorpos (left|right) default left
10432 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10436 * @cfg {String} align (left|center|right) Default left
10437 * @cfg {Boolean} forceFeedback (true|false) Default false
10440 * Create a new Input
10441 * @param {Object} config The config object
10444 Roo.bootstrap.Input = function(config){
10446 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10451 * Fires when this field receives input focus.
10452 * @param {Roo.form.Field} this
10457 * Fires when this field loses input focus.
10458 * @param {Roo.form.Field} this
10462 * @event specialkey
10463 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10464 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465 * @param {Roo.form.Field} this
10466 * @param {Roo.EventObject} e The event object
10471 * Fires just before the field blurs if the field value has changed.
10472 * @param {Roo.form.Field} this
10473 * @param {Mixed} newValue The new value
10474 * @param {Mixed} oldValue The original value
10479 * Fires after the field has been marked as invalid.
10480 * @param {Roo.form.Field} this
10481 * @param {String} msg The validation message
10486 * Fires after the field has been validated with no errors.
10487 * @param {Roo.form.Field} this
10492 * Fires after the key up
10493 * @param {Roo.form.Field} this
10494 * @param {Roo.EventObject} e The event Object
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10502 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503 automatic validation (defaults to "keyup").
10505 validationEvent : "keyup",
10507 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10509 validateOnBlur : true,
10511 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10513 validationDelay : 250,
10515 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10517 focusClass : "x-form-focus", // not needed???
10521 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10523 invalidClass : "has-warning",
10526 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10528 validClass : "has-success",
10531 * @cfg {Boolean} hasFeedback (true|false) default true
10533 hasFeedback : true,
10536 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10538 invalidFeedbackClass : "glyphicon-warning-sign",
10541 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10543 validFeedbackClass : "glyphicon-ok",
10546 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10548 selectOnFocus : false,
10551 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10555 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10560 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10562 disableKeyFilter : false,
10565 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10569 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10573 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10575 blankText : "Please complete this mandatory field",
10578 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10582 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10584 maxLength : Number.MAX_VALUE,
10586 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10588 minLengthText : "The minimum length for this field is {0}",
10590 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10592 maxLengthText : "The maximum length for this field is {0}",
10596 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597 * If available, this function will be called only after the basic validators all return true, and will be passed the
10598 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10602 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10608 * @cfg {String} regexText -- Depricated - use Invalid Text
10613 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10619 autocomplete: false,
10623 inputType : 'text',
10626 placeholder: false,
10631 preventMark: false,
10632 isFormField : true,
10635 labelAlign : false,
10638 formatedValue : false,
10639 forceFeedback : false,
10641 indicatorpos : 'left',
10651 parentLabelAlign : function()
10654 while (parent.parent()) {
10655 parent = parent.parent();
10656 if (typeof(parent.labelAlign) !='undefined') {
10657 return parent.labelAlign;
10664 getAutoCreate : function()
10666 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10672 if(this.inputType != 'hidden'){
10673 cfg.cls = 'form-group' //input-group
10679 type : this.inputType,
10680 value : this.value,
10681 cls : 'form-control',
10682 placeholder : this.placeholder || '',
10683 autocomplete : this.autocomplete || 'new-password'
10685 if (this.inputType == 'file') {
10686 input.style = 'overflow:hidden'; // why not in CSS?
10689 if(this.capture.length){
10690 input.capture = this.capture;
10693 if(this.accept.length){
10694 input.accept = this.accept + "/*";
10698 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10701 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702 input.maxLength = this.maxLength;
10705 if (this.disabled) {
10706 input.disabled=true;
10709 if (this.readOnly) {
10710 input.readonly=true;
10714 input.name = this.name;
10718 input.cls += ' input-' + this.size;
10722 ['xs','sm','md','lg'].map(function(size){
10723 if (settings[size]) {
10724 cfg.cls += ' col-' + size + '-' + settings[size];
10728 var inputblock = input;
10732 cls: 'glyphicon form-control-feedback'
10735 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10738 cls : 'has-feedback',
10746 if (this.before || this.after) {
10749 cls : 'input-group',
10753 if (this.before && typeof(this.before) == 'string') {
10755 inputblock.cn.push({
10757 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10761 if (this.before && typeof(this.before) == 'object') {
10762 this.before = Roo.factory(this.before);
10764 inputblock.cn.push({
10766 cls : 'roo-input-before input-group-prepend input-group-' +
10767 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10771 inputblock.cn.push(input);
10773 if (this.after && typeof(this.after) == 'string') {
10774 inputblock.cn.push({
10776 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10780 if (this.after && typeof(this.after) == 'object') {
10781 this.after = Roo.factory(this.after);
10783 inputblock.cn.push({
10785 cls : 'roo-input-after input-group-append input-group-' +
10786 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791 inputblock.cls += ' has-feedback';
10792 inputblock.cn.push(feedback);
10797 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798 tooltip : 'This field is required'
10800 if (this.allowBlank ) {
10801 indicator.style = this.allowBlank ? ' display:none' : '';
10803 if (align ==='left' && this.fieldLabel.length) {
10805 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10812 cls : 'control-label col-form-label',
10813 html : this.fieldLabel
10824 var labelCfg = cfg.cn[1];
10825 var contentCfg = cfg.cn[2];
10827 if(this.indicatorpos == 'right'){
10832 cls : 'control-label col-form-label',
10836 html : this.fieldLabel
10850 labelCfg = cfg.cn[0];
10851 contentCfg = cfg.cn[1];
10855 if(this.labelWidth > 12){
10856 labelCfg.style = "width: " + this.labelWidth + 'px';
10859 if(this.labelWidth < 13 && this.labelmd == 0){
10860 this.labelmd = this.labelWidth;
10863 if(this.labellg > 0){
10864 labelCfg.cls += ' col-lg-' + this.labellg;
10865 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10868 if(this.labelmd > 0){
10869 labelCfg.cls += ' col-md-' + this.labelmd;
10870 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10873 if(this.labelsm > 0){
10874 labelCfg.cls += ' col-sm-' + this.labelsm;
10875 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10878 if(this.labelxs > 0){
10879 labelCfg.cls += ' col-xs-' + this.labelxs;
10880 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10884 } else if ( this.fieldLabel.length) {
10891 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892 tooltip : 'This field is required',
10893 style : this.allowBlank ? ' display:none' : ''
10897 //cls : 'input-group-addon',
10898 html : this.fieldLabel
10906 if(this.indicatorpos == 'right'){
10911 //cls : 'input-group-addon',
10912 html : this.fieldLabel
10917 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918 tooltip : 'This field is required',
10919 style : this.allowBlank ? ' display:none' : ''
10939 if (this.parentType === 'Navbar' && this.parent().bar) {
10940 cfg.cls += ' navbar-form';
10943 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944 // on BS4 we do this only if not form
10945 cfg.cls += ' navbar-form';
10953 * return the real input element.
10955 inputEl: function ()
10957 return this.el.select('input.form-control',true).first();
10960 tooltipEl : function()
10962 return this.inputEl();
10965 indicatorEl : function()
10967 if (Roo.bootstrap.version == 4) {
10968 return false; // not enabled in v4 yet.
10971 var indicator = this.el.select('i.roo-required-indicator',true).first();
10981 setDisabled : function(v)
10983 var i = this.inputEl().dom;
10985 i.removeAttribute('disabled');
10989 i.setAttribute('disabled','true');
10991 initEvents : function()
10994 this.inputEl().on("keydown" , this.fireKey, this);
10995 this.inputEl().on("focus", this.onFocus, this);
10996 this.inputEl().on("blur", this.onBlur, this);
10998 this.inputEl().relayEvent('keyup', this);
11000 this.indicator = this.indicatorEl();
11002 if(this.indicator){
11003 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11006 // reference to original value for reset
11007 this.originalValue = this.getValue();
11008 //Roo.form.TextField.superclass.initEvents.call(this);
11009 if(this.validationEvent == 'keyup'){
11010 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011 this.inputEl().on('keyup', this.filterValidation, this);
11013 else if(this.validationEvent !== false){
11014 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11017 if(this.selectOnFocus){
11018 this.on("focus", this.preFocus, this);
11021 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022 this.inputEl().on("keypress", this.filterKeys, this);
11024 this.inputEl().relayEvent('keypress', this);
11027 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11028 this.el.on("click", this.autoSize, this);
11031 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11035 if (typeof(this.before) == 'object') {
11036 this.before.render(this.el.select('.roo-input-before',true).first());
11038 if (typeof(this.after) == 'object') {
11039 this.after.render(this.el.select('.roo-input-after',true).first());
11042 this.inputEl().on('change', this.onChange, this);
11045 filterValidation : function(e){
11046 if(!e.isNavKeyPress()){
11047 this.validationTask.delay(this.validationDelay);
11051 * Validates the field value
11052 * @return {Boolean} True if the value is valid, else false
11054 validate : function(){
11055 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056 if(this.disabled || this.validateValue(this.getRawValue())){
11061 this.markInvalid();
11067 * Validates a value according to the field's validation rules and marks the field as invalid
11068 * if the validation fails
11069 * @param {Mixed} value The value to validate
11070 * @return {Boolean} True if the value is valid, else false
11072 validateValue : function(value)
11074 if(this.getVisibilityEl().hasClass('hidden')){
11078 if(value.length < 1) { // if it's blank
11079 if(this.allowBlank){
11085 if(value.length < this.minLength){
11088 if(value.length > this.maxLength){
11092 var vt = Roo.form.VTypes;
11093 if(!vt[this.vtype](value, this)){
11097 if(typeof this.validator == "function"){
11098 var msg = this.validator(value);
11102 if (typeof(msg) == 'string') {
11103 this.invalidText = msg;
11107 if(this.regex && !this.regex.test(value)){
11115 fireKey : function(e){
11116 //Roo.log('field ' + e.getKey());
11117 if(e.isNavKeyPress()){
11118 this.fireEvent("specialkey", this, e);
11121 focus : function (selectText){
11123 this.inputEl().focus();
11124 if(selectText === true){
11125 this.inputEl().dom.select();
11131 onFocus : function(){
11132 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133 // this.el.addClass(this.focusClass);
11135 if(!this.hasFocus){
11136 this.hasFocus = true;
11137 this.startValue = this.getValue();
11138 this.fireEvent("focus", this);
11142 beforeBlur : Roo.emptyFn,
11146 onBlur : function(){
11148 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149 //this.el.removeClass(this.focusClass);
11151 this.hasFocus = false;
11152 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11155 var v = this.getValue();
11156 if(String(v) !== String(this.startValue)){
11157 this.fireEvent('change', this, v, this.startValue);
11159 this.fireEvent("blur", this);
11162 onChange : function(e)
11164 var v = this.getValue();
11165 if(String(v) !== String(this.startValue)){
11166 this.fireEvent('change', this, v, this.startValue);
11172 * Resets the current field value to the originally loaded value and clears any validation messages
11174 reset : function(){
11175 this.setValue(this.originalValue);
11179 * Returns the name of the field
11180 * @return {Mixed} name The name field
11182 getName: function(){
11186 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11187 * @return {Mixed} value The field value
11189 getValue : function(){
11191 var v = this.inputEl().getValue();
11196 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11197 * @return {Mixed} value The field value
11199 getRawValue : function(){
11200 var v = this.inputEl().getValue();
11206 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11207 * @param {Mixed} value The value to set
11209 setRawValue : function(v){
11210 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11213 selectText : function(start, end){
11214 var v = this.getRawValue();
11216 start = start === undefined ? 0 : start;
11217 end = end === undefined ? v.length : end;
11218 var d = this.inputEl().dom;
11219 if(d.setSelectionRange){
11220 d.setSelectionRange(start, end);
11221 }else if(d.createTextRange){
11222 var range = d.createTextRange();
11223 range.moveStart("character", start);
11224 range.moveEnd("character", v.length-end);
11231 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11232 * @param {Mixed} value The value to set
11234 setValue : function(v){
11237 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11243 processValue : function(value){
11244 if(this.stripCharsRe){
11245 var newValue = value.replace(this.stripCharsRe, '');
11246 if(newValue !== value){
11247 this.setRawValue(newValue);
11254 preFocus : function(){
11256 if(this.selectOnFocus){
11257 this.inputEl().dom.select();
11260 filterKeys : function(e){
11261 var k = e.getKey();
11262 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11265 var c = e.getCharCode(), cc = String.fromCharCode(c);
11266 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11269 if(!this.maskRe.test(cc)){
11274 * Clear any invalid styles/messages for this field
11276 clearInvalid : function(){
11278 if(!this.el || this.preventMark){ // not rendered
11283 this.el.removeClass([this.invalidClass, 'is-invalid']);
11285 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11287 var feedback = this.el.select('.form-control-feedback', true).first();
11290 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11295 if(this.indicator){
11296 this.indicator.removeClass('visible');
11297 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11300 this.fireEvent('valid', this);
11304 * Mark this field as valid
11306 markValid : function()
11308 if(!this.el || this.preventMark){ // not rendered...
11312 this.el.removeClass([this.invalidClass, this.validClass]);
11313 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11315 var feedback = this.el.select('.form-control-feedback', true).first();
11318 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11321 if(this.indicator){
11322 this.indicator.removeClass('visible');
11323 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11331 if(this.allowBlank && !this.getRawValue().length){
11334 if (Roo.bootstrap.version == 3) {
11335 this.el.addClass(this.validClass);
11337 this.inputEl().addClass('is-valid');
11340 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11342 var feedback = this.el.select('.form-control-feedback', true).first();
11345 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11351 this.fireEvent('valid', this);
11355 * Mark this field as invalid
11356 * @param {String} msg The validation message
11358 markInvalid : function(msg)
11360 if(!this.el || this.preventMark){ // not rendered
11364 this.el.removeClass([this.invalidClass, this.validClass]);
11365 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11367 var feedback = this.el.select('.form-control-feedback', true).first();
11370 this.el.select('.form-control-feedback', true).first().removeClass(
11371 [this.invalidFeedbackClass, this.validFeedbackClass]);
11378 if(this.allowBlank && !this.getRawValue().length){
11382 if(this.indicator){
11383 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384 this.indicator.addClass('visible');
11386 if (Roo.bootstrap.version == 3) {
11387 this.el.addClass(this.invalidClass);
11389 this.inputEl().addClass('is-invalid');
11394 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11396 var feedback = this.el.select('.form-control-feedback', true).first();
11399 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11401 if(this.getValue().length || this.forceFeedback){
11402 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11409 this.fireEvent('invalid', this, msg);
11412 SafariOnKeyDown : function(event)
11414 // this is a workaround for a password hang bug on chrome/ webkit.
11415 if (this.inputEl().dom.type != 'password') {
11419 var isSelectAll = false;
11421 if(this.inputEl().dom.selectionEnd > 0){
11422 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11424 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425 event.preventDefault();
11430 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11432 event.preventDefault();
11433 // this is very hacky as keydown always get's upper case.
11435 var cc = String.fromCharCode(event.getCharCode());
11436 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11440 adjustWidth : function(tag, w){
11441 tag = tag.toLowerCase();
11442 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444 if(tag == 'input'){
11447 if(tag == 'textarea'){
11450 }else if(Roo.isOpera){
11451 if(tag == 'input'){
11454 if(tag == 'textarea'){
11462 setFieldLabel : function(v)
11464 if(!this.rendered){
11468 if(this.indicatorEl()){
11469 var ar = this.el.select('label > span',true);
11471 if (ar.elements.length) {
11472 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473 this.fieldLabel = v;
11477 var br = this.el.select('label',true);
11479 if(br.elements.length) {
11480 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481 this.fieldLabel = v;
11485 Roo.log('Cannot Found any of label > span || label in input');
11489 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490 this.fieldLabel = v;
11505 * @class Roo.bootstrap.TextArea
11506 * @extends Roo.bootstrap.Input
11507 * Bootstrap TextArea class
11508 * @cfg {Number} cols Specifies the visible width of a text area
11509 * @cfg {Number} rows Specifies the visible number of lines in a text area
11510 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512 * @cfg {string} html text
11515 * Create a new TextArea
11516 * @param {Object} config The config object
11519 Roo.bootstrap.TextArea = function(config){
11520 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11534 getAutoCreate : function(){
11536 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11542 if(this.inputType != 'hidden'){
11543 cfg.cls = 'form-group' //input-group
11551 value : this.value || '',
11552 html: this.html || '',
11553 cls : 'form-control',
11554 placeholder : this.placeholder || ''
11558 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559 input.maxLength = this.maxLength;
11563 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11567 input.cols = this.cols;
11570 if (this.readOnly) {
11571 input.readonly = true;
11575 input.name = this.name;
11579 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11583 ['xs','sm','md','lg'].map(function(size){
11584 if (settings[size]) {
11585 cfg.cls += ' col-' + size + '-' + settings[size];
11589 var inputblock = input;
11591 if(this.hasFeedback && !this.allowBlank){
11595 cls: 'glyphicon form-control-feedback'
11599 cls : 'has-feedback',
11608 if (this.before || this.after) {
11611 cls : 'input-group',
11615 inputblock.cn.push({
11617 cls : 'input-group-addon',
11622 inputblock.cn.push(input);
11624 if(this.hasFeedback && !this.allowBlank){
11625 inputblock.cls += ' has-feedback';
11626 inputblock.cn.push(feedback);
11630 inputblock.cn.push({
11632 cls : 'input-group-addon',
11639 if (align ==='left' && this.fieldLabel.length) {
11644 cls : 'control-label',
11645 html : this.fieldLabel
11656 if(this.labelWidth > 12){
11657 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11660 if(this.labelWidth < 13 && this.labelmd == 0){
11661 this.labelmd = this.labelWidth;
11664 if(this.labellg > 0){
11665 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11669 if(this.labelmd > 0){
11670 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11674 if(this.labelsm > 0){
11675 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11679 if(this.labelxs > 0){
11680 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11684 } else if ( this.fieldLabel.length) {
11689 //cls : 'input-group-addon',
11690 html : this.fieldLabel
11708 if (this.disabled) {
11709 input.disabled=true;
11716 * return the real textarea element.
11718 inputEl: function ()
11720 return this.el.select('textarea.form-control',true).first();
11724 * Clear any invalid styles/messages for this field
11726 clearInvalid : function()
11729 if(!this.el || this.preventMark){ // not rendered
11733 var label = this.el.select('label', true).first();
11734 var icon = this.el.select('i.fa-star', true).first();
11739 this.el.removeClass( this.validClass);
11740 this.inputEl().removeClass('is-invalid');
11742 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11744 var feedback = this.el.select('.form-control-feedback', true).first();
11747 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11752 this.fireEvent('valid', this);
11756 * Mark this field as valid
11758 markValid : function()
11760 if(!this.el || this.preventMark){ // not rendered
11764 this.el.removeClass([this.invalidClass, this.validClass]);
11765 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11767 var feedback = this.el.select('.form-control-feedback', true).first();
11770 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11773 if(this.disabled || this.allowBlank){
11777 var label = this.el.select('label', true).first();
11778 var icon = this.el.select('i.fa-star', true).first();
11783 if (Roo.bootstrap.version == 3) {
11784 this.el.addClass(this.validClass);
11786 this.inputEl().addClass('is-valid');
11790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11792 var feedback = this.el.select('.form-control-feedback', true).first();
11795 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11801 this.fireEvent('valid', this);
11805 * Mark this field as invalid
11806 * @param {String} msg The validation message
11808 markInvalid : function(msg)
11810 if(!this.el || this.preventMark){ // not rendered
11814 this.el.removeClass([this.invalidClass, this.validClass]);
11815 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11817 var feedback = this.el.select('.form-control-feedback', true).first();
11820 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11823 if(this.disabled || this.allowBlank){
11827 var label = this.el.select('label', true).first();
11828 var icon = this.el.select('i.fa-star', true).first();
11830 if(!this.getValue().length && label && !icon){
11831 this.el.createChild({
11833 cls : 'text-danger fa fa-lg fa-star',
11834 tooltip : 'This field is required',
11835 style : 'margin-right:5px;'
11839 if (Roo.bootstrap.version == 3) {
11840 this.el.addClass(this.invalidClass);
11842 this.inputEl().addClass('is-invalid');
11845 // fixme ... this may be depricated need to test..
11846 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11848 var feedback = this.el.select('.form-control-feedback', true).first();
11851 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11853 if(this.getValue().length || this.forceFeedback){
11854 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11861 this.fireEvent('invalid', this, msg);
11869 * trigger field - base class for combo..
11874 * @class Roo.bootstrap.TriggerField
11875 * @extends Roo.bootstrap.Input
11876 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879 * for which you can provide a custom implementation. For example:
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11886 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11889 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11893 * Create a new TriggerField.
11894 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895 * to the base TextField)
11897 Roo.bootstrap.TriggerField = function(config){
11898 this.mimicing = false;
11899 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11904 * @cfg {String} triggerClass A CSS class to apply to the trigger
11907 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11912 * @cfg {Boolean} removable (true|false) special filter default false
11916 /** @cfg {Boolean} grow @hide */
11917 /** @cfg {Number} growMin @hide */
11918 /** @cfg {Number} growMax @hide */
11924 autoSize: Roo.emptyFn,
11928 deferHeight : true,
11931 actionMode : 'wrap',
11936 getAutoCreate : function(){
11938 var align = this.labelAlign || this.parentLabelAlign();
11943 cls: 'form-group' //input-group
11950 type : this.inputType,
11951 cls : 'form-control',
11952 autocomplete: 'new-password',
11953 placeholder : this.placeholder || ''
11957 input.name = this.name;
11960 input.cls += ' input-' + this.size;
11963 if (this.disabled) {
11964 input.disabled=true;
11967 var inputblock = input;
11969 if(this.hasFeedback && !this.allowBlank){
11973 cls: 'glyphicon form-control-feedback'
11976 if(this.removable && !this.editable ){
11978 cls : 'has-feedback',
11984 cls : 'roo-combo-removable-btn close'
11991 cls : 'has-feedback',
12000 if(this.removable && !this.editable ){
12002 cls : 'roo-removable',
12008 cls : 'roo-combo-removable-btn close'
12015 if (this.before || this.after) {
12018 cls : 'input-group',
12022 inputblock.cn.push({
12024 cls : 'input-group-addon input-group-prepend input-group-text',
12029 inputblock.cn.push(input);
12031 if(this.hasFeedback && !this.allowBlank){
12032 inputblock.cls += ' has-feedback';
12033 inputblock.cn.push(feedback);
12037 inputblock.cn.push({
12039 cls : 'input-group-addon input-group-append input-group-text',
12048 var ibwrap = inputblock;
12053 cls: 'roo-select2-choices',
12057 cls: 'roo-select2-search-field',
12069 cls: 'roo-select2-container input-group',
12074 cls: 'form-hidden-field'
12080 if(!this.multiple && this.showToggleBtn){
12086 if (this.caret != false) {
12089 cls: 'fa fa-' + this.caret
12096 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12098 Roo.bootstrap.version == 3 ? caret : '',
12101 cls: 'combobox-clear',
12115 combobox.cls += ' roo-select2-container-multi';
12119 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120 tooltip : 'This field is required'
12122 if (Roo.bootstrap.version == 4) {
12125 style : 'display:none'
12130 if (align ==='left' && this.fieldLabel.length) {
12132 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12139 cls : 'control-label',
12140 html : this.fieldLabel
12152 var labelCfg = cfg.cn[1];
12153 var contentCfg = cfg.cn[2];
12155 if(this.indicatorpos == 'right'){
12160 cls : 'control-label',
12164 html : this.fieldLabel
12178 labelCfg = cfg.cn[0];
12179 contentCfg = cfg.cn[1];
12182 if(this.labelWidth > 12){
12183 labelCfg.style = "width: " + this.labelWidth + 'px';
12186 if(this.labelWidth < 13 && this.labelmd == 0){
12187 this.labelmd = this.labelWidth;
12190 if(this.labellg > 0){
12191 labelCfg.cls += ' col-lg-' + this.labellg;
12192 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12195 if(this.labelmd > 0){
12196 labelCfg.cls += ' col-md-' + this.labelmd;
12197 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12200 if(this.labelsm > 0){
12201 labelCfg.cls += ' col-sm-' + this.labelsm;
12202 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12205 if(this.labelxs > 0){
12206 labelCfg.cls += ' col-xs-' + this.labelxs;
12207 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12210 } else if ( this.fieldLabel.length) {
12211 // Roo.log(" label");
12216 //cls : 'input-group-addon',
12217 html : this.fieldLabel
12225 if(this.indicatorpos == 'right'){
12233 html : this.fieldLabel
12247 // Roo.log(" no label && no align");
12254 ['xs','sm','md','lg'].map(function(size){
12255 if (settings[size]) {
12256 cfg.cls += ' col-' + size + '-' + settings[size];
12267 onResize : function(w, h){
12268 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 // if(typeof w == 'number'){
12270 // var x = w - this.trigger.getWidth();
12271 // this.inputEl().setWidth(this.adjustWidth('input', x));
12272 // this.trigger.setStyle('left', x+'px');
12277 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12280 getResizeEl : function(){
12281 return this.inputEl();
12285 getPositionEl : function(){
12286 return this.inputEl();
12290 alignErrorIcon : function(){
12291 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12295 initEvents : function(){
12299 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301 if(!this.multiple && this.showToggleBtn){
12302 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303 if(this.hideTrigger){
12304 this.trigger.setDisplayed(false);
12306 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12310 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12313 if(this.removable && !this.editable && !this.tickable){
12314 var close = this.closeTriggerEl();
12317 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318 close.on('click', this.removeBtnClick, this, close);
12322 //this.trigger.addClassOnOver('x-form-trigger-over');
12323 //this.trigger.addClassOnClick('x-form-trigger-click');
12326 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12330 closeTriggerEl : function()
12332 var close = this.el.select('.roo-combo-removable-btn', true).first();
12333 return close ? close : false;
12336 removeBtnClick : function(e, h, el)
12338 e.preventDefault();
12340 if(this.fireEvent("remove", this) !== false){
12342 this.fireEvent("afterremove", this)
12346 createList : function()
12348 this.list = Roo.get(document.body).createChild({
12349 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350 cls: 'typeahead typeahead-long dropdown-menu shadow',
12351 style: 'display:none'
12354 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12359 initTrigger : function(){
12364 onDestroy : function(){
12366 this.trigger.removeAllListeners();
12367 // this.trigger.remove();
12370 // this.wrap.remove();
12372 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12376 onFocus : function(){
12377 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12379 if(!this.mimicing){
12380 this.wrap.addClass('x-trigger-wrap-focus');
12381 this.mimicing = true;
12382 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383 if(this.monitorTab){
12384 this.el.on("keydown", this.checkTab, this);
12391 checkTab : function(e){
12392 if(e.getKey() == e.TAB){
12393 this.triggerBlur();
12398 onBlur : function(){
12403 mimicBlur : function(e, t){
12405 if(!this.wrap.contains(t) && this.validateBlur()){
12406 this.triggerBlur();
12412 triggerBlur : function(){
12413 this.mimicing = false;
12414 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415 if(this.monitorTab){
12416 this.el.un("keydown", this.checkTab, this);
12418 //this.wrap.removeClass('x-trigger-wrap-focus');
12419 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12423 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424 validateBlur : function(e, t){
12429 onDisable : function(){
12430 this.inputEl().dom.disabled = true;
12431 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12433 // this.wrap.addClass('x-item-disabled');
12438 onEnable : function(){
12439 this.inputEl().dom.disabled = false;
12440 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12442 // this.el.removeClass('x-item-disabled');
12447 onShow : function(){
12448 var ae = this.getActionEl();
12451 ae.dom.style.display = '';
12452 ae.dom.style.visibility = 'visible';
12458 onHide : function(){
12459 var ae = this.getActionEl();
12460 ae.dom.style.display = 'none';
12464 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12465 * by an implementing function.
12467 * @param {EventObject} e
12469 onTriggerClick : Roo.emptyFn
12477 * @class Roo.bootstrap.CardUploader
12478 * @extends Roo.bootstrap.Button
12479 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480 * @cfg {Number} errorTimeout default 3000
12481 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12482 * @cfg {Array} html The button text.
12486 * Create a new CardUploader
12487 * @param {Object} config The config object
12490 Roo.bootstrap.CardUploader = function(config){
12494 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12497 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12507 errorTimeout : 3000,
12511 fileCollection : false,
12514 getAutoCreate : function()
12518 cls :'form-group' ,
12523 //cls : 'input-group-addon',
12524 html : this.fieldLabel
12531 value : this.value,
12532 cls : 'd-none form-control'
12537 multiple : 'multiple',
12539 cls : 'd-none roo-card-upload-selector'
12543 cls : 'roo-card-uploader-button-container w-100 mb-2'
12546 cls : 'card-columns roo-card-uploader-container'
12556 getChildContainer : function() /// what children are added to.
12558 return this.containerEl;
12561 getButtonContainer : function() /// what children are added to.
12563 return this.el.select(".roo-card-uploader-button-container").first();
12566 initEvents : function()
12569 Roo.bootstrap.Input.prototype.initEvents.call(this);
12573 xns: Roo.bootstrap,
12576 container_method : 'getButtonContainer' ,
12577 html : this.html, // fix changable?
12580 'click' : function(btn, e) {
12589 this.urlAPI = (window.createObjectURL && window) ||
12590 (window.URL && URL.revokeObjectURL && URL) ||
12591 (window.webkitURL && webkitURL);
12596 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12598 this.selectorEl.on('change', this.onFileSelected, this);
12601 this.images.forEach(function(img) {
12604 this.images = false;
12606 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12612 onClick : function(e)
12614 e.preventDefault();
12616 this.selectorEl.dom.click();
12620 onFileSelected : function(e)
12622 e.preventDefault();
12624 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12628 Roo.each(this.selectorEl.dom.files, function(file){
12629 this.addFile(file);
12638 addFile : function(file)
12641 if(typeof(file) === 'string'){
12642 throw "Add file by name?"; // should not happen
12646 if(!file || !this.urlAPI){
12656 var url = _this.urlAPI.createObjectURL( file);
12659 id : Roo.bootstrap.CardUploader.ID--,
12660 is_uploaded : false,
12663 mimetype : file.type,
12670 addCard : function (data)
12672 // hidden input element?
12673 // if the file is not an image...
12674 //then we need to use something other that and header_image
12679 xns : Roo.bootstrap,
12680 xtype : 'CardFooter',
12683 xns : Roo.bootstrap,
12689 xns : Roo.bootstrap,
12691 html : String.format("<small>{0}</small>", data.title),
12692 cls : 'col-11 text-left',
12697 click : function() {
12698 this.downloadCard(data.id)
12704 xns : Roo.bootstrap,
12712 click : function() {
12713 t.removeCard(data.id)
12725 var cn = this.addxtype(
12728 xns : Roo.bootstrap,
12731 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12733 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12738 initEvents : function() {
12739 Roo.bootstrap.Card.prototype.initEvents.call(this);
12740 this.imgEl = this.el.select('.card-img-top').first();
12742 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743 this.imgEl.set({ 'pointer' : 'cursor' });
12752 // dont' really need ot update items.
12753 // this.items.push(cn);
12754 this.fileCollection.add(cn);
12755 this.updateInput();
12758 removeCard : function(id)
12761 var card = this.fileCollection.get(id);
12762 card.data.is_deleted = 1;
12763 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764 this.fileCollection.remove(card);
12765 //this.items = this.items.filter(function(e) { return e != card });
12766 // dont' really need ot update items.
12767 card.el.dom.parentNode.removeChild(card.el.dom);
12772 this.fileCollection.each(function(card) {
12773 card.el.dom.parentNode.removeChild(card.el.dom);
12775 this.fileCollection.clear();
12776 this.updateInput();
12779 updateInput : function()
12782 this.fileCollection.each(function(e) {
12786 this.inputEl().dom.value = JSON.stringify(data);
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12795 * Ext JS Library 1.1.1
12796 * Copyright(c) 2006-2007, Ext JS, LLC.
12798 * Originally Released Under LGPL - original licence link has changed is not relivant.
12801 * <script type="text/javascript">
12806 * @class Roo.data.SortTypes
12808 * Defines the default sorting (casting?) comparison functions used when sorting data.
12810 Roo.data.SortTypes = {
12812 * Default sort that does nothing
12813 * @param {Mixed} s The value being converted
12814 * @return {Mixed} The comparison value
12816 none : function(s){
12821 * The regular expression used to strip tags
12825 stripTagsRE : /<\/?[^>]+>/gi,
12828 * Strips all HTML tags to sort on text only
12829 * @param {Mixed} s The value being converted
12830 * @return {String} The comparison value
12832 asText : function(s){
12833 return String(s).replace(this.stripTagsRE, "");
12837 * Strips all HTML tags to sort on text only - Case insensitive
12838 * @param {Mixed} s The value being converted
12839 * @return {String} The comparison value
12841 asUCText : function(s){
12842 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12846 * Case insensitive string
12847 * @param {Mixed} s The value being converted
12848 * @return {String} The comparison value
12850 asUCString : function(s) {
12851 return String(s).toUpperCase();
12856 * @param {Mixed} s The value being converted
12857 * @return {Number} The comparison value
12859 asDate : function(s) {
12863 if(s instanceof Date){
12864 return s.getTime();
12866 return Date.parse(String(s));
12871 * @param {Mixed} s The value being converted
12872 * @return {Float} The comparison value
12874 asFloat : function(s) {
12875 var val = parseFloat(String(s).replace(/,/g, ""));
12884 * @param {Mixed} s The value being converted
12885 * @return {Number} The comparison value
12887 asInt : function(s) {
12888 var val = parseInt(String(s).replace(/,/g, ""));
12896 * Ext JS Library 1.1.1
12897 * Copyright(c) 2006-2007, Ext JS, LLC.
12899 * Originally Released Under LGPL - original licence link has changed is not relivant.
12902 * <script type="text/javascript">
12906 * @class Roo.data.Record
12907 * Instances of this class encapsulate both record <em>definition</em> information, and record
12908 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909 * to access Records cached in an {@link Roo.data.Store} object.<br>
12911 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12915 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12917 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918 * {@link #create}. The parameters are the same.
12919 * @param {Array} data An associative Array of data values keyed by the field name.
12920 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922 * not specified an integer id is generated.
12924 Roo.data.Record = function(data, id){
12925 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12930 * Generate a constructor for a specific record layout.
12931 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933 * Each field definition object may contain the following properties: <ul>
12934 * <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,
12935 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938 * is being used, then this is a string containing the javascript expression to reference the data relative to
12939 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941 * this may be omitted.</p></li>
12942 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943 * <ul><li>auto (Default, implies no conversion)</li>
12948 * <li>date</li></ul></p></li>
12949 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952 * by the Reader into an object that will be stored in the Record. It is passed the
12953 * following parameters:<ul>
12954 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12956 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12958 * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960 {name: 'title', mapping: 'topic_title'},
12961 {name: 'author', mapping: 'username'},
12962 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964 {name: 'lastPoster', mapping: 'user2'},
12965 {name: 'excerpt', mapping: 'post_text'}
12968 var myNewRecord = new TopicRecord({
12969 title: 'Do my job please',
12972 lastPost: new Date(),
12973 lastPoster: 'Animal',
12974 excerpt: 'No way dude!'
12976 myStore.add(myNewRecord);
12981 Roo.data.Record.create = function(o){
12982 var f = function(){
12983 f.superclass.constructor.apply(this, arguments);
12985 Roo.extend(f, Roo.data.Record);
12986 var p = f.prototype;
12987 p.fields = new Roo.util.MixedCollection(false, function(field){
12990 for(var i = 0, len = o.length; i < len; i++){
12991 p.fields.add(new Roo.data.Field(o[i]));
12993 f.getField = function(name){
12994 return p.fields.get(name);
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13004 Roo.data.Record.prototype = {
13006 * Readonly flag - true if this record has been modified.
13015 join : function(store){
13016 this.store = store;
13020 * Set the named field to the specified value.
13021 * @param {String} name The name of the field to set.
13022 * @param {Object} value The value to set the field to.
13024 set : function(name, value){
13025 if(this.data[name] == value){
13029 if(!this.modified){
13030 this.modified = {};
13032 if(typeof this.modified[name] == 'undefined'){
13033 this.modified[name] = this.data[name];
13035 this.data[name] = value;
13036 if(!this.editing && this.store){
13037 this.store.afterEdit(this);
13042 * Get the value of the named field.
13043 * @param {String} name The name of the field to get the value of.
13044 * @return {Object} The value of the field.
13046 get : function(name){
13047 return this.data[name];
13051 beginEdit : function(){
13052 this.editing = true;
13053 this.modified = {};
13057 cancelEdit : function(){
13058 this.editing = false;
13059 delete this.modified;
13063 endEdit : function(){
13064 this.editing = false;
13065 if(this.dirty && this.store){
13066 this.store.afterEdit(this);
13071 * Usually called by the {@link Roo.data.Store} which owns the Record.
13072 * Rejects all changes made to the Record since either creation, or the last commit operation.
13073 * Modified fields are reverted to their original values.
13075 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076 * of reject operations.
13078 reject : function(){
13079 var m = this.modified;
13081 if(typeof m[n] != "function"){
13082 this.data[n] = m[n];
13085 this.dirty = false;
13086 delete this.modified;
13087 this.editing = false;
13089 this.store.afterReject(this);
13094 * Usually called by the {@link Roo.data.Store} which owns the Record.
13095 * Commits all changes made to the Record since either creation, or the last commit operation.
13097 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098 * of commit operations.
13100 commit : function(){
13101 this.dirty = false;
13102 delete this.modified;
13103 this.editing = false;
13105 this.store.afterCommit(this);
13110 hasError : function(){
13111 return this.error != null;
13115 clearError : function(){
13120 * Creates a copy of this record.
13121 * @param {String} id (optional) A new record id if you don't want to use this record's id
13124 copy : function(newId) {
13125 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13129 * Ext JS Library 1.1.1
13130 * Copyright(c) 2006-2007, Ext JS, LLC.
13132 * Originally Released Under LGPL - original licence link has changed is not relivant.
13135 * <script type="text/javascript">
13141 * @class Roo.data.Store
13142 * @extends Roo.util.Observable
13143 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13146 * 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
13147 * has no knowledge of the format of the data returned by the Proxy.<br>
13149 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150 * instances from the data object. These records are cached and made available through accessor functions.
13152 * Creates a new Store.
13153 * @param {Object} config A config object containing the objects needed for the Store to access data,
13154 * and read the data into Records.
13156 Roo.data.Store = function(config){
13157 this.data = new Roo.util.MixedCollection(false);
13158 this.data.getKey = function(o){
13161 this.baseParams = {};
13163 this.paramNames = {
13168 "multisort" : "_multisort"
13171 if(config && config.data){
13172 this.inlineData = config.data;
13173 delete config.data;
13176 Roo.apply(this, config);
13178 if(this.reader){ // reader passed
13179 this.reader = Roo.factory(this.reader, Roo.data);
13180 this.reader.xmodule = this.xmodule || false;
13181 if(!this.recordType){
13182 this.recordType = this.reader.recordType;
13184 if(this.reader.onMetaChange){
13185 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13189 if(this.recordType){
13190 this.fields = this.recordType.prototype.fields;
13192 this.modified = [];
13196 * @event datachanged
13197 * Fires when the data cache has changed, and a widget which is using this Store
13198 * as a Record cache should refresh its view.
13199 * @param {Store} this
13201 datachanged : true,
13203 * @event metachange
13204 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205 * @param {Store} this
13206 * @param {Object} meta The JSON metadata
13211 * Fires when Records have been added to the Store
13212 * @param {Store} this
13213 * @param {Roo.data.Record[]} records The array of Records added
13214 * @param {Number} index The index at which the record(s) were added
13219 * Fires when a Record has been removed from the Store
13220 * @param {Store} this
13221 * @param {Roo.data.Record} record The Record that was removed
13222 * @param {Number} index The index at which the record was removed
13227 * Fires when a Record has been updated
13228 * @param {Store} this
13229 * @param {Roo.data.Record} record The Record that was updated
13230 * @param {String} operation The update operation being performed. Value may be one of:
13232 Roo.data.Record.EDIT
13233 Roo.data.Record.REJECT
13234 Roo.data.Record.COMMIT
13240 * Fires when the data cache has been cleared.
13241 * @param {Store} this
13245 * @event beforeload
13246 * Fires before a request is made for a new data object. If the beforeload handler returns false
13247 * the load action will be canceled.
13248 * @param {Store} this
13249 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253 * @event beforeloadadd
13254 * Fires after a new set of Records has been loaded.
13255 * @param {Store} this
13256 * @param {Roo.data.Record[]} records The Records that were loaded
13257 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13259 beforeloadadd : true,
13262 * Fires after a new set of Records has been loaded, before they are added to the store.
13263 * @param {Store} this
13264 * @param {Roo.data.Record[]} records The Records that were loaded
13265 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266 * @params {Object} return from reader
13270 * @event loadexception
13271 * Fires if an exception occurs in the Proxy during loading.
13272 * Called with the signature of the Proxy's "loadexception" event.
13273 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13276 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277 * @param {Object} load options
13278 * @param {Object} jsonData from your request (normally this contains the Exception)
13280 loadexception : true
13284 this.proxy = Roo.factory(this.proxy, Roo.data);
13285 this.proxy.xmodule = this.xmodule || false;
13286 this.relayEvents(this.proxy, ["loadexception"]);
13288 this.sortToggle = {};
13289 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13291 Roo.data.Store.superclass.constructor.call(this);
13293 if(this.inlineData){
13294 this.loadData(this.inlineData);
13295 delete this.inlineData;
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13301 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13302 * without a remote query - used by combo/forms at present.
13306 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13309 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13312 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13316 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317 * on any HTTP request
13320 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13323 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13327 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13330 remoteSort : false,
13333 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334 * loaded or when a record is removed. (defaults to false).
13336 pruneModifiedRecords : false,
13339 lastOptions : null,
13342 * Add Records to the Store and fires the add event.
13343 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13345 add : function(records){
13346 records = [].concat(records);
13347 for(var i = 0, len = records.length; i < len; i++){
13348 records[i].join(this);
13350 var index = this.data.length;
13351 this.data.addAll(records);
13352 this.fireEvent("add", this, records, index);
13356 * Remove a Record from the Store and fires the remove event.
13357 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13359 remove : function(record){
13360 var index = this.data.indexOf(record);
13361 this.data.removeAt(index);
13363 if(this.pruneModifiedRecords){
13364 this.modified.remove(record);
13366 this.fireEvent("remove", this, record, index);
13370 * Remove all Records from the Store and fires the clear event.
13372 removeAll : function(){
13374 if(this.pruneModifiedRecords){
13375 this.modified = [];
13377 this.fireEvent("clear", this);
13381 * Inserts Records to the Store at the given index and fires the add event.
13382 * @param {Number} index The start index at which to insert the passed Records.
13383 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13385 insert : function(index, records){
13386 records = [].concat(records);
13387 for(var i = 0, len = records.length; i < len; i++){
13388 this.data.insert(index, records[i]);
13389 records[i].join(this);
13391 this.fireEvent("add", this, records, index);
13395 * Get the index within the cache of the passed Record.
13396 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397 * @return {Number} The index of the passed Record. Returns -1 if not found.
13399 indexOf : function(record){
13400 return this.data.indexOf(record);
13404 * Get the index within the cache of the Record with the passed id.
13405 * @param {String} id The id of the Record to find.
13406 * @return {Number} The index of the Record. Returns -1 if not found.
13408 indexOfId : function(id){
13409 return this.data.indexOfKey(id);
13413 * Get the Record with the specified id.
13414 * @param {String} id The id of the Record to find.
13415 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13417 getById : function(id){
13418 return this.data.key(id);
13422 * Get the Record at the specified index.
13423 * @param {Number} index The index of the Record to find.
13424 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13426 getAt : function(index){
13427 return this.data.itemAt(index);
13431 * Returns a range of Records between specified indices.
13432 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434 * @return {Roo.data.Record[]} An array of Records
13436 getRange : function(start, end){
13437 return this.data.getRange(start, end);
13441 storeOptions : function(o){
13442 o = Roo.apply({}, o);
13445 this.lastOptions = o;
13449 * Loads the Record cache from the configured Proxy using the configured Reader.
13451 * If using remote paging, then the first load call must specify the <em>start</em>
13452 * and <em>limit</em> properties in the options.params property to establish the initial
13453 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13455 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456 * and this call will return before the new data has been loaded. Perform any post-processing
13457 * in a callback function, or in a "load" event handler.</strong>
13459 * @param {Object} options An object containing properties which control loading options:<ul>
13460 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462 * passed the following arguments:<ul>
13463 * <li>r : Roo.data.Record[]</li>
13464 * <li>options: Options object from the load call</li>
13465 * <li>success: Boolean success indicator</li></ul></li>
13466 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13470 load : function(options){
13471 options = options || {};
13472 if(this.fireEvent("beforeload", this, options) !== false){
13473 this.storeOptions(options);
13474 var p = Roo.apply(options.params || {}, this.baseParams);
13475 // if meta was not loaded from remote source.. try requesting it.
13476 if (!this.reader.metaFromRemote) {
13477 p._requestMeta = 1;
13479 if(this.sortInfo && this.remoteSort){
13480 var pn = this.paramNames;
13481 p[pn["sort"]] = this.sortInfo.field;
13482 p[pn["dir"]] = this.sortInfo.direction;
13484 if (this.multiSort) {
13485 var pn = this.paramNames;
13486 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13489 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13494 * Reloads the Record cache from the configured Proxy using the configured Reader and
13495 * the options from the last load operation performed.
13496 * @param {Object} options (optional) An object containing properties which may override the options
13497 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498 * the most recently used options are reused).
13500 reload : function(options){
13501 this.load(Roo.applyIf(options||{}, this.lastOptions));
13505 // Called as a callback by the Reader during a load operation.
13506 loadRecords : function(o, options, success){
13507 if(!o || success === false){
13508 if(success !== false){
13509 this.fireEvent("load", this, [], options, o);
13511 if(options.callback){
13512 options.callback.call(options.scope || this, [], options, false);
13516 // if data returned failure - throw an exception.
13517 if (o.success === false) {
13518 // show a message if no listener is registered.
13519 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13522 // loadmask wil be hooked into this..
13523 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13526 var r = o.records, t = o.totalRecords || r.length;
13528 this.fireEvent("beforeloadadd", this, r, options, o);
13530 if(!options || options.add !== true){
13531 if(this.pruneModifiedRecords){
13532 this.modified = [];
13534 for(var i = 0, len = r.length; i < len; i++){
13538 this.data = this.snapshot;
13539 delete this.snapshot;
13542 this.data.addAll(r);
13543 this.totalLength = t;
13545 this.fireEvent("datachanged", this);
13547 this.totalLength = Math.max(t, this.data.length+r.length);
13551 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13553 var e = new Roo.data.Record({});
13555 e.set(this.parent.displayField, this.parent.emptyTitle);
13556 e.set(this.parent.valueField, '');
13561 this.fireEvent("load", this, r, options, o);
13562 if(options.callback){
13563 options.callback.call(options.scope || this, r, options, true);
13569 * Loads data from a passed data block. A Reader which understands the format of the data
13570 * must have been configured in the constructor.
13571 * @param {Object} data The data block from which to read the Records. The format of the data expected
13572 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13575 loadData : function(o, append){
13576 var r = this.reader.readRecords(o);
13577 this.loadRecords(r, {add: append}, true);
13581 * using 'cn' the nested child reader read the child array into it's child stores.
13582 * @param {Object} rec The record with a 'children array
13584 loadDataFromChildren : function(rec)
13586 this.loadData(this.reader.toLoadData(rec));
13591 * Gets the number of cached records.
13593 * <em>If using paging, this may not be the total size of the dataset. If the data object
13594 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595 * the data set size</em>
13597 getCount : function(){
13598 return this.data.length || 0;
13602 * Gets the total number of records in the dataset as returned by the server.
13604 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605 * the dataset size</em>
13607 getTotalCount : function(){
13608 return this.totalLength || 0;
13612 * Returns the sort state of the Store as an object with two properties:
13614 field {String} The name of the field by which the Records are sorted
13615 direction {String} The sort order, "ASC" or "DESC"
13618 getSortState : function(){
13619 return this.sortInfo;
13623 applySort : function(){
13624 if(this.sortInfo && !this.remoteSort){
13625 var s = this.sortInfo, f = s.field;
13626 var st = this.fields.get(f).sortType;
13627 var fn = function(r1, r2){
13628 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13631 this.data.sort(s.direction, fn);
13632 if(this.snapshot && this.snapshot != this.data){
13633 this.snapshot.sort(s.direction, fn);
13639 * Sets the default sort column and order to be used by the next load operation.
13640 * @param {String} fieldName The name of the field to sort by.
13641 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13643 setDefaultSort : function(field, dir){
13644 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13648 * Sort the Records.
13649 * If remote sorting is used, the sort is performed on the server, and the cache is
13650 * reloaded. If local sorting is used, the cache is sorted internally.
13651 * @param {String} fieldName The name of the field to sort by.
13652 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13654 sort : function(fieldName, dir){
13655 var f = this.fields.get(fieldName);
13657 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13659 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13665 this.sortToggle[f.name] = dir;
13666 this.sortInfo = {field: f.name, direction: dir};
13667 if(!this.remoteSort){
13669 this.fireEvent("datachanged", this);
13671 this.load(this.lastOptions);
13676 * Calls the specified function for each of the Records in the cache.
13677 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678 * Returning <em>false</em> aborts and exits the iteration.
13679 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13681 each : function(fn, scope){
13682 this.data.each(fn, scope);
13686 * Gets all records modified since the last commit. Modified records are persisted across load operations
13687 * (e.g., during paging).
13688 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13690 getModifiedRecords : function(){
13691 return this.modified;
13695 createFilterFn : function(property, value, anyMatch){
13696 if(!value.exec){ // not a regex
13697 value = String(value);
13698 if(value.length == 0){
13701 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13703 return function(r){
13704 return value.test(r.data[property]);
13709 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710 * @param {String} property A field on your records
13711 * @param {Number} start The record index to start at (defaults to 0)
13712 * @param {Number} end The last record index to include (defaults to length - 1)
13713 * @return {Number} The sum
13715 sum : function(property, start, end){
13716 var rs = this.data.items, v = 0;
13717 start = start || 0;
13718 end = (end || end === 0) ? end : rs.length-1;
13720 for(var i = start; i <= end; i++){
13721 v += (rs[i].data[property] || 0);
13727 * Filter the records by a specified property.
13728 * @param {String} field A field on your records
13729 * @param {String/RegExp} value Either a string that the field
13730 * should start with or a RegExp to test against the field
13731 * @param {Boolean} anyMatch True to match any part not just the beginning
13733 filter : function(property, value, anyMatch){
13734 var fn = this.createFilterFn(property, value, anyMatch);
13735 return fn ? this.filterBy(fn) : this.clearFilter();
13739 * Filter by a function. The specified function will be called with each
13740 * record in this data source. If the function returns true the record is included,
13741 * otherwise it is filtered.
13742 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743 * @param {Object} scope (optional) The scope of the function (defaults to this)
13745 filterBy : function(fn, scope){
13746 this.snapshot = this.snapshot || this.data;
13747 this.data = this.queryBy(fn, scope||this);
13748 this.fireEvent("datachanged", this);
13752 * Query the records by a specified property.
13753 * @param {String} field A field on your records
13754 * @param {String/RegExp} value Either a string that the field
13755 * should start with or a RegExp to test against the field
13756 * @param {Boolean} anyMatch True to match any part not just the beginning
13757 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13759 query : function(property, value, anyMatch){
13760 var fn = this.createFilterFn(property, value, anyMatch);
13761 return fn ? this.queryBy(fn) : this.data.clone();
13765 * Query by a function. The specified function will be called with each
13766 * record in this data source. If the function returns true the record is included
13768 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769 * @param {Object} scope (optional) The scope of the function (defaults to this)
13770 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13772 queryBy : function(fn, scope){
13773 var data = this.snapshot || this.data;
13774 return data.filterBy(fn, scope||this);
13778 * Collects unique values for a particular dataIndex from this store.
13779 * @param {String} dataIndex The property to collect
13780 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782 * @return {Array} An array of the unique values
13784 collect : function(dataIndex, allowNull, bypassFilter){
13785 var d = (bypassFilter === true && this.snapshot) ?
13786 this.snapshot.items : this.data.items;
13787 var v, sv, r = [], l = {};
13788 for(var i = 0, len = d.length; i < len; i++){
13789 v = d[i].data[dataIndex];
13791 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13800 * Revert to a view of the Record cache with no filtering applied.
13801 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13803 clearFilter : function(suppressEvent){
13804 if(this.snapshot && this.snapshot != this.data){
13805 this.data = this.snapshot;
13806 delete this.snapshot;
13807 if(suppressEvent !== true){
13808 this.fireEvent("datachanged", this);
13814 afterEdit : function(record){
13815 if(this.modified.indexOf(record) == -1){
13816 this.modified.push(record);
13818 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13822 afterReject : function(record){
13823 this.modified.remove(record);
13824 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13828 afterCommit : function(record){
13829 this.modified.remove(record);
13830 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13834 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13837 commitChanges : function(){
13838 var m = this.modified.slice(0);
13839 this.modified = [];
13840 for(var i = 0, len = m.length; i < len; i++){
13846 * Cancel outstanding changes on all changed records.
13848 rejectChanges : function(){
13849 var m = this.modified.slice(0);
13850 this.modified = [];
13851 for(var i = 0, len = m.length; i < len; i++){
13856 onMetaChange : function(meta, rtype, o){
13857 this.recordType = rtype;
13858 this.fields = rtype.prototype.fields;
13859 delete this.snapshot;
13860 this.sortInfo = meta.sortInfo || this.sortInfo;
13861 this.modified = [];
13862 this.fireEvent('metachange', this, this.reader.meta);
13865 moveIndex : function(data, type)
13867 var index = this.indexOf(data);
13869 var newIndex = index + type;
13873 this.insert(newIndex, data);
13878 * Ext JS Library 1.1.1
13879 * Copyright(c) 2006-2007, Ext JS, LLC.
13881 * Originally Released Under LGPL - original licence link has changed is not relivant.
13884 * <script type="text/javascript">
13888 * @class Roo.data.SimpleStore
13889 * @extends Roo.data.Store
13890 * Small helper class to make creating Stores from Array data easier.
13891 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892 * @cfg {Array} fields An array of field definition objects, or field name strings.
13893 * @cfg {Object} an existing reader (eg. copied from another store)
13894 * @cfg {Array} data The multi-dimensional array of data
13896 * @param {Object} config
13898 Roo.data.SimpleStore = function(config)
13900 Roo.data.SimpleStore.superclass.constructor.call(this, {
13902 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13905 Roo.data.Record.create(config.fields)
13907 proxy : new Roo.data.MemoryProxy(config.data)
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13913 * Ext JS Library 1.1.1
13914 * Copyright(c) 2006-2007, Ext JS, LLC.
13916 * Originally Released Under LGPL - original licence link has changed is not relivant.
13919 * <script type="text/javascript">
13924 * @extends Roo.data.Store
13925 * @class Roo.data.JsonStore
13926 * Small helper class to make creating Stores for JSON data easier. <br/>
13928 var store = new Roo.data.JsonStore({
13929 url: 'get-images.php',
13931 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13934 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935 * JsonReader and HttpProxy (unless inline data is provided).</b>
13936 * @cfg {Array} fields An array of field definition objects, or field name strings.
13938 * @param {Object} config
13940 Roo.data.JsonStore = function(c){
13941 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943 reader: new Roo.data.JsonReader(c, c.fields)
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13948 * Ext JS Library 1.1.1
13949 * Copyright(c) 2006-2007, Ext JS, LLC.
13951 * Originally Released Under LGPL - original licence link has changed is not relivant.
13954 * <script type="text/javascript">
13958 Roo.data.Field = function(config){
13959 if(typeof config == "string"){
13960 config = {name: config};
13962 Roo.apply(this, config);
13965 this.type = "auto";
13968 var st = Roo.data.SortTypes;
13969 // named sortTypes are supported, here we look them up
13970 if(typeof this.sortType == "string"){
13971 this.sortType = st[this.sortType];
13974 // set default sortType for strings and dates
13975 if(!this.sortType){
13978 this.sortType = st.asUCString;
13981 this.sortType = st.asDate;
13984 this.sortType = st.none;
13989 var stripRe = /[\$,%]/g;
13991 // prebuilt conversion function for this field, instead of
13992 // switching every time we're reading a value
13994 var cv, dateFormat = this.dateFormat;
13999 cv = function(v){ return v; };
14002 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14006 return v !== undefined && v !== null && v !== '' ?
14007 parseInt(String(v).replace(stripRe, ""), 10) : '';
14012 return v !== undefined && v !== null && v !== '' ?
14013 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14018 cv = function(v){ return v === true || v === "true" || v == 1; };
14025 if(v instanceof Date){
14029 if(dateFormat == "timestamp"){
14030 return new Date(v*1000);
14032 return Date.parseDate(v, dateFormat);
14034 var parsed = Date.parse(v);
14035 return parsed ? new Date(parsed) : null;
14044 Roo.data.Field.prototype = {
14052 * Ext JS Library 1.1.1
14053 * Copyright(c) 2006-2007, Ext JS, LLC.
14055 * Originally Released Under LGPL - original licence link has changed is not relivant.
14058 * <script type="text/javascript">
14061 // Base class for reading structured data from a data source. This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14065 * @class Roo.data.DataReader
14066 * Base class for reading structured data from a data source. This class is intended to be
14067 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14070 Roo.data.DataReader = function(meta, recordType){
14074 this.recordType = recordType instanceof Array ?
14075 Roo.data.Record.create(recordType) : recordType;
14078 Roo.data.DataReader.prototype = {
14081 readerType : 'Data',
14083 * Create an empty record
14084 * @param {Object} data (optional) - overlay some values
14085 * @return {Roo.data.Record} record created.
14087 newRow : function(d) {
14089 this.recordType.prototype.fields.each(function(c) {
14091 case 'int' : da[c.name] = 0; break;
14092 case 'date' : da[c.name] = new Date(); break;
14093 case 'float' : da[c.name] = 0.0; break;
14094 case 'boolean' : da[c.name] = false; break;
14095 default : da[c.name] = ""; break;
14099 return new this.recordType(Roo.apply(da, d));
14105 * Ext JS Library 1.1.1
14106 * Copyright(c) 2006-2007, Ext JS, LLC.
14108 * Originally Released Under LGPL - original licence link has changed is not relivant.
14111 * <script type="text/javascript">
14115 * @class Roo.data.DataProxy
14116 * @extends Roo.data.Observable
14117 * This class is an abstract base class for implementations which provide retrieval of
14118 * unformatted data objects.<br>
14120 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121 * (of the appropriate type which knows how to parse the data object) to provide a block of
14122 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14124 * Custom implementations must implement the load method as described in
14125 * {@link Roo.data.HttpProxy#load}.
14127 Roo.data.DataProxy = function(){
14130 * @event beforeload
14131 * Fires before a network request is made to retrieve a data object.
14132 * @param {Object} This DataProxy object.
14133 * @param {Object} params The params parameter to the load function.
14138 * Fires before the load method's callback is called.
14139 * @param {Object} This DataProxy object.
14140 * @param {Object} o The data object.
14141 * @param {Object} arg The callback argument object passed to the load function.
14145 * @event loadexception
14146 * Fires if an Exception occurs during data retrieval.
14147 * @param {Object} This DataProxy object.
14148 * @param {Object} o The data object.
14149 * @param {Object} arg The callback argument object passed to the load function.
14150 * @param {Object} e The Exception.
14152 loadexception : true
14154 Roo.data.DataProxy.superclass.constructor.call(this);
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14160 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14164 * Ext JS Library 1.1.1
14165 * Copyright(c) 2006-2007, Ext JS, LLC.
14167 * Originally Released Under LGPL - original licence link has changed is not relivant.
14170 * <script type="text/javascript">
14173 * @class Roo.data.MemoryProxy
14174 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175 * to the Reader when its load method is called.
14177 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14179 Roo.data.MemoryProxy = function(data){
14183 Roo.data.MemoryProxy.superclass.constructor.call(this);
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14190 * Load data from the requested source (in this case an in-memory
14191 * data object passed to the constructor), read the data object into
14192 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193 * process that block using the passed callback.
14194 * @param {Object} params This parameter is not used by the MemoryProxy class.
14195 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196 * object into a block of Roo.data.Records.
14197 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198 * The function must be passed <ul>
14199 * <li>The Record block object</li>
14200 * <li>The "arg" argument from the load function</li>
14201 * <li>A boolean success indicator</li>
14203 * @param {Object} scope The scope in which to call the callback
14204 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14206 load : function(params, reader, callback, scope, arg){
14207 params = params || {};
14210 result = reader.readRecords(params.data ? params.data :this.data);
14212 this.fireEvent("loadexception", this, arg, null, e);
14213 callback.call(scope, null, arg, false);
14216 callback.call(scope, result, arg, true);
14220 update : function(params, records){
14225 * Ext JS Library 1.1.1
14226 * Copyright(c) 2006-2007, Ext JS, LLC.
14228 * Originally Released Under LGPL - original licence link has changed is not relivant.
14231 * <script type="text/javascript">
14234 * @class Roo.data.HttpProxy
14235 * @extends Roo.data.DataProxy
14236 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237 * configured to reference a certain URL.<br><br>
14239 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240 * from which the running page was served.<br><br>
14242 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14244 * Be aware that to enable the browser to parse an XML document, the server must set
14245 * the Content-Type header in the HTTP response to "text/xml".
14247 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249 * will be used to make the request.
14251 Roo.data.HttpProxy = function(conn){
14252 Roo.data.HttpProxy.superclass.constructor.call(this);
14253 // is conn a conn config or a real conn?
14255 this.useAjax = !conn || !conn.events;
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260 // thse are take from connection...
14263 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14266 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267 * extra parameters to each request made by this object. (defaults to undefined)
14270 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271 * to each request made by this object. (defaults to undefined)
14274 * @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)
14277 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14280 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14286 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14290 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292 * a finer-grained basis than the DataProxy events.
14294 getConnection : function(){
14295 return this.useAjax ? Roo.Ajax : this.conn;
14299 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301 * process that block using the passed callback.
14302 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303 * for the request to the remote server.
14304 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305 * object into a block of Roo.data.Records.
14306 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307 * The function must be passed <ul>
14308 * <li>The Record block object</li>
14309 * <li>The "arg" argument from the load function</li>
14310 * <li>A boolean success indicator</li>
14312 * @param {Object} scope The scope in which to call the callback
14313 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14315 load : function(params, reader, callback, scope, arg){
14316 if(this.fireEvent("beforeload", this, params) !== false){
14318 params : params || {},
14320 callback : callback,
14325 callback : this.loadResponse,
14329 Roo.applyIf(o, this.conn);
14330 if(this.activeRequest){
14331 Roo.Ajax.abort(this.activeRequest);
14333 this.activeRequest = Roo.Ajax.request(o);
14335 this.conn.request(o);
14338 callback.call(scope||this, null, arg, false);
14343 loadResponse : function(o, success, response){
14344 delete this.activeRequest;
14346 this.fireEvent("loadexception", this, o, response);
14347 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14352 result = o.reader.read(response);
14354 this.fireEvent("loadexception", this, o, response, e);
14355 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14359 this.fireEvent("load", this, o, o.request.arg);
14360 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14364 update : function(dataSet){
14369 updateResponse : function(dataSet){
14374 * Ext JS Library 1.1.1
14375 * Copyright(c) 2006-2007, Ext JS, LLC.
14377 * Originally Released Under LGPL - original licence link has changed is not relivant.
14380 * <script type="text/javascript">
14384 * @class Roo.data.ScriptTagProxy
14385 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386 * other than the originating domain of the running page.<br><br>
14388 * <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
14389 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14391 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392 * source code that is used as the source inside a <script> tag.<br><br>
14394 * In order for the browser to process the returned data, the server must wrap the data object
14395 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397 * depending on whether the callback name was passed:
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14404 response.setContentType("text/javascript");
14406 response.setContentType("application/x-json");
14408 Writer out = response.getWriter();
14410 out.write(cb + "(");
14412 out.print(dataBlock.toJsonString());
14419 * @param {Object} config A configuration object.
14421 Roo.data.ScriptTagProxy = function(config){
14422 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423 Roo.apply(this, config);
14424 this.head = document.getElementsByTagName("head")[0];
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14431 * @cfg {String} url The URL from which to request the data object.
14434 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14438 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439 * the server the name of the callback function set up by the load call to process the returned data object.
14440 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441 * javascript output which calls this named function passing the data object as its only parameter.
14443 callbackParam : "callback",
14445 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446 * name to the request.
14451 * Load data from the configured URL, read the data object into
14452 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453 * process that block using the passed callback.
14454 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455 * for the request to the remote server.
14456 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457 * object into a block of Roo.data.Records.
14458 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459 * The function must be passed <ul>
14460 * <li>The Record block object</li>
14461 * <li>The "arg" argument from the load function</li>
14462 * <li>A boolean success indicator</li>
14464 * @param {Object} scope The scope in which to call the callback
14465 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14467 load : function(params, reader, callback, scope, arg){
14468 if(this.fireEvent("beforeload", this, params) !== false){
14470 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14472 var url = this.url;
14473 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14475 url += "&_dc=" + (new Date().getTime());
14477 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14480 cb : "stcCallback"+transId,
14481 scriptId : "stcScript"+transId,
14485 callback : callback,
14491 window[trans.cb] = function(o){
14492 conn.handleResponse(o, trans);
14495 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14497 if(this.autoAbort !== false){
14501 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14503 var script = document.createElement("script");
14504 script.setAttribute("src", url);
14505 script.setAttribute("type", "text/javascript");
14506 script.setAttribute("id", trans.scriptId);
14507 this.head.appendChild(script);
14509 this.trans = trans;
14511 callback.call(scope||this, null, arg, false);
14516 isLoading : function(){
14517 return this.trans ? true : false;
14521 * Abort the current server request.
14523 abort : function(){
14524 if(this.isLoading()){
14525 this.destroyTrans(this.trans);
14530 destroyTrans : function(trans, isLoaded){
14531 this.head.removeChild(document.getElementById(trans.scriptId));
14532 clearTimeout(trans.timeoutId);
14534 window[trans.cb] = undefined;
14536 delete window[trans.cb];
14539 // if hasn't been loaded, wait for load to remove it to prevent script error
14540 window[trans.cb] = function(){
14541 window[trans.cb] = undefined;
14543 delete window[trans.cb];
14550 handleResponse : function(o, trans){
14551 this.trans = false;
14552 this.destroyTrans(trans, true);
14555 result = trans.reader.readRecords(o);
14557 this.fireEvent("loadexception", this, o, trans.arg, e);
14558 trans.callback.call(trans.scope||window, null, trans.arg, false);
14561 this.fireEvent("load", this, o, trans.arg);
14562 trans.callback.call(trans.scope||window, result, trans.arg, true);
14566 handleFailure : function(trans){
14567 this.trans = false;
14568 this.destroyTrans(trans, false);
14569 this.fireEvent("loadexception", this, null, trans.arg);
14570 trans.callback.call(trans.scope||window, null, trans.arg, false);
14574 * Ext JS Library 1.1.1
14575 * Copyright(c) 2006-2007, Ext JS, LLC.
14577 * Originally Released Under LGPL - original licence link has changed is not relivant.
14580 * <script type="text/javascript">
14584 * @class Roo.data.JsonReader
14585 * @extends Roo.data.DataReader
14586 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587 * based on mappings in a provided Roo.data.Record constructor.
14589 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590 * in the reply previously.
14595 var RecordDef = Roo.data.Record.create([
14596 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14597 {name: 'occupation'} // This field will use "occupation" as the mapping.
14599 var myReader = new Roo.data.JsonReader({
14600 totalProperty: "results", // The property which contains the total dataset size (optional)
14601 root: "rows", // The property which contains an Array of row objects
14602 id: "id" // The property within each row object that provides an ID for the record (optional)
14606 * This would consume a JSON file like this:
14608 { 'results': 2, 'rows': [
14609 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14613 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615 * paged from the remote server.
14616 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617 * @cfg {String} root name of the property which contains the Array of row objects.
14618 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619 * @cfg {Array} fields Array of field definition objects
14621 * Create a new JsonReader
14622 * @param {Object} meta Metadata configuration options
14623 * @param {Object} recordType Either an Array of field definition objects,
14624 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14626 Roo.data.JsonReader = function(meta, recordType){
14629 // set some defaults:
14630 Roo.applyIf(meta, {
14631 totalProperty: 'total',
14632 successProperty : 'success',
14637 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14641 readerType : 'Json',
14644 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14645 * Used by Store query builder to append _requestMeta to params.
14648 metaFromRemote : false,
14650 * This method is only used by a DataProxy which has retrieved data from a remote server.
14651 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652 * @return {Object} data A data block which is used by an Roo.data.Store object as
14653 * a cache of Roo.data.Records.
14655 read : function(response){
14656 var json = response.responseText;
14658 var o = /* eval:var:o */ eval("("+json+")");
14660 throw {message: "JsonReader.read: Json object not found"};
14666 this.metaFromRemote = true;
14667 this.meta = o.metaData;
14668 this.recordType = Roo.data.Record.create(o.metaData.fields);
14669 this.onMetaChange(this.meta, this.recordType, o);
14671 return this.readRecords(o);
14674 // private function a store will implement
14675 onMetaChange : function(meta, recordType, o){
14682 simpleAccess: function(obj, subsc) {
14689 getJsonAccessor: function(){
14691 return function(expr) {
14693 return(re.test(expr))
14694 ? new Function("obj", "return obj." + expr)
14699 return Roo.emptyFn;
14704 * Create a data block containing Roo.data.Records from an XML document.
14705 * @param {Object} o An object which contains an Array of row objects in the property specified
14706 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707 * which contains the total size of the dataset.
14708 * @return {Object} data A data block which is used by an Roo.data.Store object as
14709 * a cache of Roo.data.Records.
14711 readRecords : function(o){
14713 * After any data loads, the raw JSON data is available for further custom processing.
14717 var s = this.meta, Record = this.recordType,
14718 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14720 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14722 if(s.totalProperty) {
14723 this.getTotal = this.getJsonAccessor(s.totalProperty);
14725 if(s.successProperty) {
14726 this.getSuccess = this.getJsonAccessor(s.successProperty);
14728 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14730 var g = this.getJsonAccessor(s.id);
14731 this.getId = function(rec) {
14733 return (r === undefined || r === "") ? null : r;
14736 this.getId = function(){return null;};
14739 for(var jj = 0; jj < fl; jj++){
14741 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742 this.ef[jj] = this.getJsonAccessor(map);
14746 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747 if(s.totalProperty){
14748 var vt = parseInt(this.getTotal(o), 10);
14753 if(s.successProperty){
14754 var vs = this.getSuccess(o);
14755 if(vs === false || vs === 'false'){
14760 for(var i = 0; i < c; i++){
14763 var id = this.getId(n);
14764 for(var j = 0; j < fl; j++){
14766 var v = this.ef[j](n);
14768 Roo.log('missing convert for ' + f.name);
14772 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14774 var record = new Record(values, id);
14776 records[i] = record;
14782 totalRecords : totalRecords
14785 // used when loading children.. @see loadDataFromChildren
14786 toLoadData: function(rec)
14788 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790 return { data : data, total : data.length };
14795 * Ext JS Library 1.1.1
14796 * Copyright(c) 2006-2007, Ext JS, LLC.
14798 * Originally Released Under LGPL - original licence link has changed is not relivant.
14801 * <script type="text/javascript">
14805 * @class Roo.data.ArrayReader
14806 * @extends Roo.data.DataReader
14807 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808 * Each element of that Array represents a row of data fields. The
14809 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14814 var RecordDef = Roo.data.Record.create([
14815 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14816 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14818 var myReader = new Roo.data.ArrayReader({
14819 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14823 * This would consume an Array like this:
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14829 * Create a new JsonReader
14830 * @param {Object} meta Metadata configuration options.
14831 * @param {Object|Array} recordType Either an Array of field definition objects
14833 * @cfg {Array} fields Array of field definition objects
14834 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835 * as specified to {@link Roo.data.Record#create},
14836 * or an {@link Roo.data.Record} object
14839 * created using {@link Roo.data.Record#create}.
14841 Roo.data.ArrayReader = function(meta, recordType)
14843 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14849 * Create a data block containing Roo.data.Records from an XML document.
14850 * @param {Object} o An Array of row objects which represents the dataset.
14851 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852 * a cache of Roo.data.Records.
14854 readRecords : function(o)
14856 var sid = this.meta ? this.meta.id : null;
14857 var recordType = this.recordType, fields = recordType.prototype.fields;
14860 for(var i = 0; i < root.length; i++){
14863 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864 for(var j = 0, jlen = fields.length; j < jlen; j++){
14865 var f = fields.items[j];
14866 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14869 values[f.name] = v;
14871 var record = new recordType(values, id);
14873 records[records.length] = record;
14877 totalRecords : records.length
14880 // used when loading children.. @see loadDataFromChildren
14881 toLoadData: function(rec)
14883 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14895 * @class Roo.bootstrap.ComboBox
14896 * @extends Roo.bootstrap.TriggerField
14897 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898 * @cfg {Boolean} append (true|false) default false
14899 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904 * @cfg {Boolean} animate default true
14905 * @cfg {Boolean} emptyResultText only for touch device
14906 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907 * @cfg {String} emptyTitle default ''
14908 * @cfg {Number} width fixed with? experimental
14910 * Create a new ComboBox.
14911 * @param {Object} config Configuration options
14913 Roo.bootstrap.ComboBox = function(config){
14914 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14918 * Fires when the dropdown list is expanded
14919 * @param {Roo.bootstrap.ComboBox} combo This combo box
14924 * Fires when the dropdown list is collapsed
14925 * @param {Roo.bootstrap.ComboBox} combo This combo box
14929 * @event beforeselect
14930 * Fires before a list item is selected. Return false to cancel the selection.
14931 * @param {Roo.bootstrap.ComboBox} combo This combo box
14932 * @param {Roo.data.Record} record The data record returned from the underlying store
14933 * @param {Number} index The index of the selected item in the dropdown list
14935 'beforeselect' : true,
14938 * Fires when a list item is selected
14939 * @param {Roo.bootstrap.ComboBox} combo This combo box
14940 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941 * @param {Number} index The index of the selected item in the dropdown list
14945 * @event beforequery
14946 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947 * The event object passed has these properties:
14948 * @param {Roo.bootstrap.ComboBox} combo This combo box
14949 * @param {String} query The query
14950 * @param {Boolean} forceAll true to force "all" query
14951 * @param {Boolean} cancel true to cancel the query
14952 * @param {Object} e The query event object
14954 'beforequery': true,
14957 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958 * @param {Roo.bootstrap.ComboBox} combo This combo box
14963 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964 * @param {Roo.bootstrap.ComboBox} combo This combo box
14965 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14970 * Fires when the remove value from the combobox array
14971 * @param {Roo.bootstrap.ComboBox} combo This combo box
14975 * @event afterremove
14976 * Fires when the remove value from the combobox array
14977 * @param {Roo.bootstrap.ComboBox} combo This combo box
14979 'afterremove' : true,
14981 * @event specialfilter
14982 * Fires when specialfilter
14983 * @param {Roo.bootstrap.ComboBox} combo This combo box
14985 'specialfilter' : true,
14988 * Fires when tick the element
14989 * @param {Roo.bootstrap.ComboBox} combo This combo box
14993 * @event touchviewdisplay
14994 * Fires when touch view require special display (default is using displayField)
14995 * @param {Roo.bootstrap.ComboBox} combo This combo box
14996 * @param {Object} cfg set html .
14998 'touchviewdisplay' : true
15003 this.tickItems = [];
15005 this.selectedIndex = -1;
15006 if(this.mode == 'local'){
15007 if(config.queryDelay === undefined){
15008 this.queryDelay = 10;
15010 if(config.minChars === undefined){
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15019 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020 * rendering into an Roo.Editor, defaults to false)
15023 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15027 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15030 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031 * the dropdown list (defaults to undefined, with no header element)
15035 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15039 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15041 listWidth: undefined,
15043 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044 * mode = 'remote' or 'text' if mode = 'local')
15046 displayField: undefined,
15049 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050 * mode = 'remote' or 'value' if mode = 'local').
15051 * Note: use of a valueField requires the user make a selection
15052 * in order for a value to be mapped.
15054 valueField: undefined,
15056 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15061 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062 * field's data value (defaults to the underlying DOM element's name)
15064 hiddenName: undefined,
15066 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15070 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15072 selectedClass: 'active',
15075 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15079 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080 * anchor positions (defaults to 'tl-bl')
15082 listAlign: 'tl-bl?',
15084 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15088 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15089 * query specified by the allQuery config option (defaults to 'query')
15091 triggerAction: 'query',
15093 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094 * (defaults to 4, does not apply if editable = false)
15098 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15103 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15108 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15113 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15114 * when editable = true (defaults to false)
15116 selectOnFocus:false,
15118 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15120 queryParam: 'query',
15122 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15123 * when mode = 'remote' (defaults to 'Loading...')
15125 loadingText: 'Loading...',
15127 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15131 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15135 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136 * traditional select (defaults to true)
15140 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15144 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15148 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149 * listWidth has a higher value)
15153 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154 * allow the user to set arbitrary text into the field (defaults to false)
15156 forceSelection:false,
15158 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159 * if typeAhead = true (defaults to 250)
15161 typeAheadDelay : 250,
15163 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15166 valueNotFoundText : undefined,
15168 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15170 blockFocus : false,
15173 * @cfg {Boolean} disableClear Disable showing of clear button.
15175 disableClear : false,
15177 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15179 alwaysQuery : false,
15182 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15187 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15189 invalidClass : "has-warning",
15192 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15194 validClass : "has-success",
15197 * @cfg {Boolean} specialFilter (true|false) special filter default false
15199 specialFilter : false,
15202 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15204 mobileTouchView : true,
15207 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15209 useNativeIOS : false,
15212 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15214 mobile_restrict_height : false,
15216 ios_options : false,
15228 btnPosition : 'right',
15229 triggerList : true,
15230 showToggleBtn : true,
15232 emptyResultText: 'Empty',
15233 triggerText : 'Select',
15237 // element that contains real text value.. (when hidden is used..)
15239 getAutoCreate : function()
15244 * Render classic select for iso
15247 if(Roo.isIOS && this.useNativeIOS){
15248 cfg = this.getAutoCreateNativeIOS();
15256 if(Roo.isTouch && this.mobileTouchView){
15257 cfg = this.getAutoCreateTouchView();
15264 if(!this.tickable){
15265 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15270 * ComboBox with tickable selections
15273 var align = this.labelAlign || this.parentLabelAlign();
15276 cls : 'form-group roo-combobox-tickable' //input-group
15279 var btn_text_select = '';
15280 var btn_text_done = '';
15281 var btn_text_cancel = '';
15283 if (this.btn_text_show) {
15284 btn_text_select = 'Select';
15285 btn_text_done = 'Done';
15286 btn_text_cancel = 'Cancel';
15291 cls : 'tickable-buttons',
15296 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297 //html : this.triggerText
15298 html: btn_text_select
15304 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15306 html: btn_text_done
15312 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15314 html: btn_text_cancel
15320 buttons.cn.unshift({
15322 cls: 'roo-select2-search-field-input'
15328 Roo.each(buttons.cn, function(c){
15330 c.cls += ' btn-' + _this.size;
15333 if (_this.disabled) {
15340 style : 'display: contents',
15345 cls: 'form-hidden-field'
15349 cls: 'roo-select2-choices',
15353 cls: 'roo-select2-search-field',
15364 cls: 'roo-select2-container input-group roo-select2-container-multi',
15370 // cls: 'typeahead typeahead-long dropdown-menu',
15371 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15376 if(this.hasFeedback && !this.allowBlank){
15380 cls: 'glyphicon form-control-feedback'
15383 combobox.cn.push(feedback);
15390 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391 tooltip : 'This field is required'
15393 if (Roo.bootstrap.version == 4) {
15396 style : 'display:none'
15399 if (align ==='left' && this.fieldLabel.length) {
15401 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15408 cls : 'control-label col-form-label',
15409 html : this.fieldLabel
15421 var labelCfg = cfg.cn[1];
15422 var contentCfg = cfg.cn[2];
15425 if(this.indicatorpos == 'right'){
15431 cls : 'control-label col-form-label',
15435 html : this.fieldLabel
15451 labelCfg = cfg.cn[0];
15452 contentCfg = cfg.cn[1];
15456 if(this.labelWidth > 12){
15457 labelCfg.style = "width: " + this.labelWidth + 'px';
15459 if(this.width * 1 > 0){
15460 contentCfg.style = "width: " + this.width + 'px';
15462 if(this.labelWidth < 13 && this.labelmd == 0){
15463 this.labelmd = this.labelWidth;
15466 if(this.labellg > 0){
15467 labelCfg.cls += ' col-lg-' + this.labellg;
15468 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15471 if(this.labelmd > 0){
15472 labelCfg.cls += ' col-md-' + this.labelmd;
15473 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15476 if(this.labelsm > 0){
15477 labelCfg.cls += ' col-sm-' + this.labelsm;
15478 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15481 if(this.labelxs > 0){
15482 labelCfg.cls += ' col-xs-' + this.labelxs;
15483 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15487 } else if ( this.fieldLabel.length) {
15488 // Roo.log(" label");
15493 //cls : 'input-group-addon',
15494 html : this.fieldLabel
15499 if(this.indicatorpos == 'right'){
15503 //cls : 'input-group-addon',
15504 html : this.fieldLabel
15514 // Roo.log(" no label && no align");
15521 ['xs','sm','md','lg'].map(function(size){
15522 if (settings[size]) {
15523 cfg.cls += ' col-' + size + '-' + settings[size];
15531 _initEventsCalled : false,
15534 initEvents: function()
15536 if (this._initEventsCalled) { // as we call render... prevent looping...
15539 this._initEventsCalled = true;
15542 throw "can not find store for combo";
15545 this.indicator = this.indicatorEl();
15547 this.store = Roo.factory(this.store, Roo.data);
15548 this.store.parent = this;
15550 // if we are building from html. then this element is so complex, that we can not really
15551 // use the rendered HTML.
15552 // so we have to trash and replace the previous code.
15553 if (Roo.XComponent.build_from_html) {
15554 // remove this element....
15555 var e = this.el.dom, k=0;
15556 while (e ) { e = e.previousSibling; ++k;}
15561 this.rendered = false;
15563 this.render(this.parent().getChildContainer(true), k);
15566 if(Roo.isIOS && this.useNativeIOS){
15567 this.initIOSView();
15575 if(Roo.isTouch && this.mobileTouchView){
15576 this.initTouchView();
15581 this.initTickableEvents();
15585 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15587 if(this.hiddenName){
15589 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15591 this.hiddenField.dom.value =
15592 this.hiddenValue !== undefined ? this.hiddenValue :
15593 this.value !== undefined ? this.value : '';
15595 // prevent input submission
15596 this.el.dom.removeAttribute('name');
15597 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15602 // this.el.dom.setAttribute('autocomplete', 'off');
15605 var cls = 'x-combo-list';
15607 //this.list = new Roo.Layer({
15608 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15614 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615 _this.list.setWidth(lw);
15618 this.list.on('mouseover', this.onViewOver, this);
15619 this.list.on('mousemove', this.onViewMove, this);
15620 this.list.on('scroll', this.onViewScroll, this);
15623 this.list.swallowEvent('mousewheel');
15624 this.assetHeight = 0;
15627 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628 this.assetHeight += this.header.getHeight();
15631 this.innerList = this.list.createChild({cls:cls+'-inner'});
15632 this.innerList.on('mouseover', this.onViewOver, this);
15633 this.innerList.on('mousemove', this.onViewMove, this);
15634 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15636 if(this.allowBlank && !this.pageSize && !this.disableClear){
15637 this.footer = this.list.createChild({cls:cls+'-ft'});
15638 this.pageTb = new Roo.Toolbar(this.footer);
15642 this.footer = this.list.createChild({cls:cls+'-ft'});
15643 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644 {pageSize: this.pageSize});
15648 if (this.pageTb && this.allowBlank && !this.disableClear) {
15650 this.pageTb.add(new Roo.Toolbar.Fill(), {
15651 cls: 'x-btn-icon x-btn-clear',
15653 handler: function()
15656 _this.clearValue();
15657 _this.onSelect(false, -1);
15662 this.assetHeight += this.footer.getHeight();
15667 this.tpl = Roo.bootstrap.version == 4 ?
15668 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15669 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15672 this.view = new Roo.View(this.list, this.tpl, {
15673 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15675 //this.view.wrapEl.setDisplayed(false);
15676 this.view.on('click', this.onViewClick, this);
15679 this.store.on('beforeload', this.onBeforeLoad, this);
15680 this.store.on('load', this.onLoad, this);
15681 this.store.on('loadexception', this.onLoadException, this);
15683 if(this.resizable){
15684 this.resizer = new Roo.Resizable(this.list, {
15685 pinned:true, handles:'se'
15687 this.resizer.on('resize', function(r, w, h){
15688 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689 this.listWidth = w;
15690 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691 this.restrictHeight();
15693 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15696 if(!this.editable){
15697 this.editable = true;
15698 this.setEditable(false);
15703 if (typeof(this.events.add.listeners) != 'undefined') {
15705 this.addicon = this.wrap.createChild(
15706 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15708 this.addicon.on('click', function(e) {
15709 this.fireEvent('add', this);
15712 if (typeof(this.events.edit.listeners) != 'undefined') {
15714 this.editicon = this.wrap.createChild(
15715 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15716 if (this.addicon) {
15717 this.editicon.setStyle('margin-left', '40px');
15719 this.editicon.on('click', function(e) {
15721 // we fire even if inothing is selected..
15722 this.fireEvent('edit', this, this.lastData );
15728 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729 "up" : function(e){
15730 this.inKeyMode = true;
15734 "down" : function(e){
15735 if(!this.isExpanded()){
15736 this.onTriggerClick();
15738 this.inKeyMode = true;
15743 "enter" : function(e){
15744 // this.onViewClick();
15748 if(this.fireEvent("specialkey", this, e)){
15749 this.onViewClick(false);
15755 "esc" : function(e){
15759 "tab" : function(e){
15762 if(this.fireEvent("specialkey", this, e)){
15763 this.onViewClick(false);
15771 doRelay : function(foo, bar, hname){
15772 if(hname == 'down' || this.scope.isExpanded()){
15773 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15782 this.queryDelay = Math.max(this.queryDelay || 10,
15783 this.mode == 'local' ? 10 : 250);
15786 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15788 if(this.typeAhead){
15789 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15791 if(this.editable !== false){
15792 this.inputEl().on("keyup", this.onKeyUp, this);
15794 if(this.forceSelection){
15795 this.inputEl().on('blur', this.doForce, this);
15799 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15804 initTickableEvents: function()
15808 if(this.hiddenName){
15810 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15812 this.hiddenField.dom.value =
15813 this.hiddenValue !== undefined ? this.hiddenValue :
15814 this.value !== undefined ? this.value : '';
15816 // prevent input submission
15817 this.el.dom.removeAttribute('name');
15818 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15823 // this.list = this.el.select('ul.dropdown-menu',true).first();
15825 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827 if(this.triggerList){
15828 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15831 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15834 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15837 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15840 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15845 this.cancelBtn.hide();
15850 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851 _this.list.setWidth(lw);
15854 this.list.on('mouseover', this.onViewOver, this);
15855 this.list.on('mousemove', this.onViewMove, this);
15857 this.list.on('scroll', this.onViewScroll, this);
15860 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15861 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15864 this.view = new Roo.View(this.list, this.tpl, {
15869 selectedClass: this.selectedClass
15872 //this.view.wrapEl.setDisplayed(false);
15873 this.view.on('click', this.onViewClick, this);
15877 this.store.on('beforeload', this.onBeforeLoad, this);
15878 this.store.on('load', this.onLoad, this);
15879 this.store.on('loadexception', this.onLoadException, this);
15882 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883 "up" : function(e){
15884 this.inKeyMode = true;
15888 "down" : function(e){
15889 this.inKeyMode = true;
15893 "enter" : function(e){
15894 if(this.fireEvent("specialkey", this, e)){
15895 this.onViewClick(false);
15901 "esc" : function(e){
15902 this.onTickableFooterButtonClick(e, false, false);
15905 "tab" : function(e){
15906 this.fireEvent("specialkey", this, e);
15908 this.onTickableFooterButtonClick(e, false, false);
15915 doRelay : function(e, fn, key){
15916 if(this.scope.isExpanded()){
15917 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15926 this.queryDelay = Math.max(this.queryDelay || 10,
15927 this.mode == 'local' ? 10 : 250);
15930 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15932 if(this.typeAhead){
15933 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15936 if(this.editable !== false){
15937 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15940 this.indicator = this.indicatorEl();
15942 if(this.indicator){
15943 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944 this.indicator.hide();
15949 onDestroy : function(){
15951 this.view.setStore(null);
15952 this.view.el.removeAllListeners();
15953 this.view.el.remove();
15954 this.view.purgeListeners();
15957 this.list.dom.innerHTML = '';
15961 this.store.un('beforeload', this.onBeforeLoad, this);
15962 this.store.un('load', this.onLoad, this);
15963 this.store.un('loadexception', this.onLoadException, this);
15965 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15969 fireKey : function(e){
15970 if(e.isNavKeyPress() && !this.list.isVisible()){
15971 this.fireEvent("specialkey", this, e);
15976 onResize: function(w, h)
15980 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15982 // if(typeof w != 'number'){
15983 // // we do not handle it!?!?
15986 // var tw = this.trigger.getWidth();
15987 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15990 // this.inputEl().setWidth( this.adjustWidth('input', x));
15992 // //this.trigger.setStyle('left', x+'px');
15994 // if(this.list && this.listWidth === undefined){
15995 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 // this.list.setWidth(lw);
15997 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16005 * Allow or prevent the user from directly editing the field text. If false is passed,
16006 * the user will only be able to select from the items defined in the dropdown list. This method
16007 * is the runtime equivalent of setting the 'editable' config option at config time.
16008 * @param {Boolean} value True to allow the user to directly edit the field text
16010 setEditable : function(value){
16011 if(value == this.editable){
16014 this.editable = value;
16016 this.inputEl().dom.setAttribute('readOnly', true);
16017 this.inputEl().on('mousedown', this.onTriggerClick, this);
16018 this.inputEl().addClass('x-combo-noedit');
16020 this.inputEl().dom.setAttribute('readOnly', false);
16021 this.inputEl().un('mousedown', this.onTriggerClick, this);
16022 this.inputEl().removeClass('x-combo-noedit');
16028 onBeforeLoad : function(combo,opts){
16029 if(!this.hasFocus){
16033 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16035 this.restrictHeight();
16036 this.selectedIndex = -1;
16040 onLoad : function(){
16042 this.hasQuery = false;
16044 if(!this.hasFocus){
16048 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049 this.loading.hide();
16052 if(this.store.getCount() > 0){
16055 this.restrictHeight();
16056 if(this.lastQuery == this.allQuery){
16057 if(this.editable && !this.tickable){
16058 this.inputEl().dom.select();
16062 !this.selectByValue(this.value, true) &&
16065 !this.store.lastOptions ||
16066 typeof(this.store.lastOptions.add) == 'undefined' ||
16067 this.store.lastOptions.add != true
16070 this.select(0, true);
16073 if(this.autoFocus){
16076 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077 this.taTask.delay(this.typeAheadDelay);
16081 this.onEmptyResults();
16087 onLoadException : function()
16089 this.hasQuery = false;
16091 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092 this.loading.hide();
16095 if(this.tickable && this.editable){
16100 // only causes errors at present
16101 //Roo.log(this.store.reader.jsonData);
16102 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16104 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16110 onTypeAhead : function(){
16111 if(this.store.getCount() > 0){
16112 var r = this.store.getAt(0);
16113 var newValue = r.data[this.displayField];
16114 var len = newValue.length;
16115 var selStart = this.getRawValue().length;
16117 if(selStart != len){
16118 this.setRawValue(newValue);
16119 this.selectText(selStart, newValue.length);
16125 onSelect : function(record, index){
16127 if(this.fireEvent('beforeselect', this, record, index) !== false){
16129 this.setFromData(index > -1 ? record.data : false);
16132 this.fireEvent('select', this, record, index);
16137 * Returns the currently selected field value or empty string if no value is set.
16138 * @return {String} value The selected value
16140 getValue : function()
16142 if(Roo.isIOS && this.useNativeIOS){
16143 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16147 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16150 if(this.valueField){
16151 return typeof this.value != 'undefined' ? this.value : '';
16153 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16157 getRawValue : function()
16159 if(Roo.isIOS && this.useNativeIOS){
16160 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16163 var v = this.inputEl().getValue();
16169 * Clears any text/value currently set in the field
16171 clearValue : function(){
16173 if(this.hiddenField){
16174 this.hiddenField.dom.value = '';
16177 this.setRawValue('');
16178 this.lastSelectionText = '';
16179 this.lastData = false;
16181 var close = this.closeTriggerEl();
16192 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16193 * will be displayed in the field. If the value does not match the data value of an existing item,
16194 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195 * Otherwise the field will be blank (although the value will still be set).
16196 * @param {String} value The value to match
16198 setValue : function(v)
16200 if(Roo.isIOS && this.useNativeIOS){
16201 this.setIOSValue(v);
16211 if(this.valueField){
16212 var r = this.findRecord(this.valueField, v);
16214 text = r.data[this.displayField];
16215 }else if(this.valueNotFoundText !== undefined){
16216 text = this.valueNotFoundText;
16219 this.lastSelectionText = text;
16220 if(this.hiddenField){
16221 this.hiddenField.dom.value = v;
16223 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16226 var close = this.closeTriggerEl();
16229 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16235 * @property {Object} the last set data for the element
16240 * Sets the value of the field based on a object which is related to the record format for the store.
16241 * @param {Object} value the value to set as. or false on reset?
16243 setFromData : function(o){
16250 var dv = ''; // display value
16251 var vv = ''; // value value..
16253 if (this.displayField) {
16254 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16256 // this is an error condition!!!
16257 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16260 if(this.valueField){
16261 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16264 var close = this.closeTriggerEl();
16267 if(dv.length || vv * 1 > 0){
16269 this.blockFocus=true;
16275 if(this.hiddenField){
16276 this.hiddenField.dom.value = vv;
16278 this.lastSelectionText = dv;
16279 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16283 // no hidden field.. - we store the value in 'value', but still display
16284 // display field!!!!
16285 this.lastSelectionText = dv;
16286 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16293 reset : function(){
16294 // overridden so that last data is reset..
16301 this.setValue(this.originalValue);
16302 //this.clearInvalid();
16303 this.lastData = false;
16305 this.view.clearSelections();
16311 findRecord : function(prop, value){
16313 if(this.store.getCount() > 0){
16314 this.store.each(function(r){
16315 if(r.data[prop] == value){
16325 getName: function()
16327 // returns hidden if it's set..
16328 if (!this.rendered) {return ''};
16329 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16333 onViewMove : function(e, t){
16334 this.inKeyMode = false;
16338 onViewOver : function(e, t){
16339 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16342 var item = this.view.findItemFromChild(t);
16345 var index = this.view.indexOf(item);
16346 this.select(index, false);
16351 onViewClick : function(view, doFocus, el, e)
16353 var index = this.view.getSelectedIndexes()[0];
16355 var r = this.store.getAt(index);
16359 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16366 Roo.each(this.tickItems, function(v,k){
16368 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16370 _this.tickItems.splice(k, 1);
16372 if(typeof(e) == 'undefined' && view == false){
16373 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16385 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386 this.tickItems.push(r.data);
16389 if(typeof(e) == 'undefined' && view == false){
16390 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16397 this.onSelect(r, index);
16399 if(doFocus !== false && !this.blockFocus){
16400 this.inputEl().focus();
16405 restrictHeight : function(){
16406 //this.innerList.dom.style.height = '';
16407 //var inner = this.innerList.dom;
16408 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410 //this.list.beginUpdate();
16411 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412 this.list.alignTo(this.inputEl(), this.listAlign);
16413 this.list.alignTo(this.inputEl(), this.listAlign);
16414 //this.list.endUpdate();
16418 onEmptyResults : function(){
16420 if(this.tickable && this.editable){
16421 this.hasFocus = false;
16422 this.restrictHeight();
16430 * Returns true if the dropdown list is expanded, else false.
16432 isExpanded : function(){
16433 return this.list.isVisible();
16437 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439 * @param {String} value The data value of the item to select
16440 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441 * selected item if it is not currently in view (defaults to true)
16442 * @return {Boolean} True if the value matched an item in the list, else false
16444 selectByValue : function(v, scrollIntoView){
16445 if(v !== undefined && v !== null){
16446 var r = this.findRecord(this.valueField || this.displayField, v);
16448 this.select(this.store.indexOf(r), scrollIntoView);
16456 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458 * @param {Number} index The zero-based index of the list item to select
16459 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460 * selected item if it is not currently in view (defaults to true)
16462 select : function(index, scrollIntoView){
16463 this.selectedIndex = index;
16464 this.view.select(index);
16465 if(scrollIntoView !== false){
16466 var el = this.view.getNode(index);
16468 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16471 this.list.scrollChildIntoView(el, false);
16477 selectNext : function(){
16478 var ct = this.store.getCount();
16480 if(this.selectedIndex == -1){
16482 }else if(this.selectedIndex < ct-1){
16483 this.select(this.selectedIndex+1);
16489 selectPrev : function(){
16490 var ct = this.store.getCount();
16492 if(this.selectedIndex == -1){
16494 }else if(this.selectedIndex != 0){
16495 this.select(this.selectedIndex-1);
16501 onKeyUp : function(e){
16502 if(this.editable !== false && !e.isSpecialKey()){
16503 this.lastKey = e.getKey();
16504 this.dqTask.delay(this.queryDelay);
16509 validateBlur : function(){
16510 return !this.list || !this.list.isVisible();
16514 initQuery : function(){
16516 var v = this.getRawValue();
16518 if(this.tickable && this.editable){
16519 v = this.tickableInputEl().getValue();
16526 doForce : function(){
16527 if(this.inputEl().dom.value.length > 0){
16528 this.inputEl().dom.value =
16529 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16535 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16536 * query allowing the query action to be canceled if needed.
16537 * @param {String} query The SQL query to execute
16538 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16540 * saved in the current store (defaults to false)
16542 doQuery : function(q, forceAll){
16544 if(q === undefined || q === null){
16549 forceAll: forceAll,
16553 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16558 forceAll = qe.forceAll;
16559 if(forceAll === true || (q.length >= this.minChars)){
16561 this.hasQuery = true;
16563 if(this.lastQuery != q || this.alwaysQuery){
16564 this.lastQuery = q;
16565 if(this.mode == 'local'){
16566 this.selectedIndex = -1;
16568 this.store.clearFilter();
16571 if(this.specialFilter){
16572 this.fireEvent('specialfilter', this);
16577 this.store.filter(this.displayField, q);
16580 this.store.fireEvent("datachanged", this.store);
16587 this.store.baseParams[this.queryParam] = q;
16589 var options = {params : this.getParams(q)};
16592 options.add = true;
16593 options.params.start = this.page * this.pageSize;
16596 this.store.load(options);
16599 * this code will make the page width larger, at the beginning, the list not align correctly,
16600 * we should expand the list on onLoad
16601 * so command out it
16606 this.selectedIndex = -1;
16611 this.loadNext = false;
16615 getParams : function(q){
16617 //p[this.queryParam] = q;
16621 p.limit = this.pageSize;
16627 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16629 collapse : function(){
16630 if(!this.isExpanded()){
16636 this.hasFocus = false;
16640 this.cancelBtn.hide();
16641 this.trigger.show();
16644 this.tickableInputEl().dom.value = '';
16645 this.tickableInputEl().blur();
16650 Roo.get(document).un('mousedown', this.collapseIf, this);
16651 Roo.get(document).un('mousewheel', this.collapseIf, this);
16652 if (!this.editable) {
16653 Roo.get(document).un('keydown', this.listKeyPress, this);
16655 this.fireEvent('collapse', this);
16661 collapseIf : function(e){
16662 var in_combo = e.within(this.el);
16663 var in_list = e.within(this.list);
16664 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16666 if (in_combo || in_list || is_list) {
16667 //e.stopPropagation();
16672 this.onTickableFooterButtonClick(e, false, false);
16680 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16682 expand : function(){
16684 if(this.isExpanded() || !this.hasFocus){
16688 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689 this.list.setWidth(lw);
16695 this.restrictHeight();
16699 this.tickItems = Roo.apply([], this.item);
16702 this.cancelBtn.show();
16703 this.trigger.hide();
16706 this.tickableInputEl().focus();
16711 Roo.get(document).on('mousedown', this.collapseIf, this);
16712 Roo.get(document).on('mousewheel', this.collapseIf, this);
16713 if (!this.editable) {
16714 Roo.get(document).on('keydown', this.listKeyPress, this);
16717 this.fireEvent('expand', this);
16721 // Implements the default empty TriggerField.onTriggerClick function
16722 onTriggerClick : function(e)
16724 Roo.log('trigger click');
16726 if(this.disabled || !this.triggerList){
16731 this.loadNext = false;
16733 if(this.isExpanded()){
16735 if (!this.blockFocus) {
16736 this.inputEl().focus();
16740 this.hasFocus = true;
16741 if(this.triggerAction == 'all') {
16742 this.doQuery(this.allQuery, true);
16744 this.doQuery(this.getRawValue());
16746 if (!this.blockFocus) {
16747 this.inputEl().focus();
16752 onTickableTriggerClick : function(e)
16759 this.loadNext = false;
16760 this.hasFocus = true;
16762 if(this.triggerAction == 'all') {
16763 this.doQuery(this.allQuery, true);
16765 this.doQuery(this.getRawValue());
16769 onSearchFieldClick : function(e)
16771 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772 this.onTickableFooterButtonClick(e, false, false);
16776 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16781 this.loadNext = false;
16782 this.hasFocus = true;
16784 if(this.triggerAction == 'all') {
16785 this.doQuery(this.allQuery, true);
16787 this.doQuery(this.getRawValue());
16791 listKeyPress : function(e)
16793 //Roo.log('listkeypress');
16794 // scroll to first matching element based on key pres..
16795 if (e.isSpecialKey()) {
16798 var k = String.fromCharCode(e.getKey()).toUpperCase();
16801 var csel = this.view.getSelectedNodes();
16802 var cselitem = false;
16804 var ix = this.view.indexOf(csel[0]);
16805 cselitem = this.store.getAt(ix);
16806 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16812 this.store.each(function(v) {
16814 // start at existing selection.
16815 if (cselitem.id == v.id) {
16821 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822 match = this.store.indexOf(v);
16828 if (match === false) {
16829 return true; // no more action?
16832 this.view.select(match);
16833 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834 sn.scrollIntoView(sn.dom.parentNode, false);
16837 onViewScroll : function(e, t){
16839 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){
16843 this.hasQuery = true;
16845 this.loading = this.list.select('.loading', true).first();
16847 if(this.loading === null){
16848 this.list.createChild({
16850 cls: 'loading roo-select2-more-results roo-select2-active',
16851 html: 'Loading more results...'
16854 this.loading = this.list.select('.loading', true).first();
16856 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16858 this.loading.hide();
16861 this.loading.show();
16866 this.loadNext = true;
16868 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16873 addItem : function(o)
16875 var dv = ''; // display value
16877 if (this.displayField) {
16878 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16880 // this is an error condition!!!
16881 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16888 var choice = this.choices.createChild({
16890 cls: 'roo-select2-search-choice',
16899 cls: 'roo-select2-search-choice-close fa fa-times',
16904 }, this.searchField);
16906 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16908 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16916 this.inputEl().dom.value = '';
16921 onRemoveItem : function(e, _self, o)
16923 e.preventDefault();
16925 this.lastItem = Roo.apply([], this.item);
16927 var index = this.item.indexOf(o.data) * 1;
16930 Roo.log('not this item?!');
16934 this.item.splice(index, 1);
16939 this.fireEvent('remove', this, e);
16945 syncValue : function()
16947 if(!this.item.length){
16954 Roo.each(this.item, function(i){
16955 if(_this.valueField){
16956 value.push(i[_this.valueField]);
16963 this.value = value.join(',');
16965 if(this.hiddenField){
16966 this.hiddenField.dom.value = this.value;
16969 this.store.fireEvent("datachanged", this.store);
16974 clearItem : function()
16976 if(!this.multiple){
16982 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16990 if(this.tickable && !Roo.isTouch){
16991 this.view.refresh();
16995 inputEl: function ()
16997 if(Roo.isIOS && this.useNativeIOS){
16998 return this.el.select('select.roo-ios-select', true).first();
17001 if(Roo.isTouch && this.mobileTouchView){
17002 return this.el.select('input.form-control',true).first();
17006 return this.searchField;
17009 return this.el.select('input.form-control',true).first();
17012 onTickableFooterButtonClick : function(e, btn, el)
17014 e.preventDefault();
17016 this.lastItem = Roo.apply([], this.item);
17018 if(btn && btn.name == 'cancel'){
17019 this.tickItems = Roo.apply([], this.item);
17028 Roo.each(this.tickItems, function(o){
17036 validate : function()
17038 if(this.getVisibilityEl().hasClass('hidden')){
17042 var v = this.getRawValue();
17045 v = this.getValue();
17048 if(this.disabled || this.allowBlank || v.length){
17053 this.markInvalid();
17057 tickableInputEl : function()
17059 if(!this.tickable || !this.editable){
17060 return this.inputEl();
17063 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17067 getAutoCreateTouchView : function()
17072 cls: 'form-group' //input-group
17078 type : this.inputType,
17079 cls : 'form-control x-combo-noedit',
17080 autocomplete: 'new-password',
17081 placeholder : this.placeholder || '',
17086 input.name = this.name;
17090 input.cls += ' input-' + this.size;
17093 if (this.disabled) {
17094 input.disabled = true;
17098 cls : 'roo-combobox-wrap',
17105 inputblock.cls += ' input-group';
17107 inputblock.cn.unshift({
17109 cls : 'input-group-addon input-group-prepend input-group-text',
17114 if(this.removable && !this.multiple){
17115 inputblock.cls += ' roo-removable';
17117 inputblock.cn.push({
17120 cls : 'roo-combo-removable-btn close'
17124 if(this.hasFeedback && !this.allowBlank){
17126 inputblock.cls += ' has-feedback';
17128 inputblock.cn.push({
17130 cls: 'glyphicon form-control-feedback'
17137 inputblock.cls += (this.before) ? '' : ' input-group';
17139 inputblock.cn.push({
17141 cls : 'input-group-addon input-group-append input-group-text',
17147 var ibwrap = inputblock;
17152 cls: 'roo-select2-choices',
17156 cls: 'roo-select2-search-field',
17169 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17174 cls: 'form-hidden-field'
17180 if(!this.multiple && this.showToggleBtn){
17186 if (this.caret != false) {
17189 cls: 'fa fa-' + this.caret
17196 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17198 Roo.bootstrap.version == 3 ? caret : '',
17201 cls: 'combobox-clear',
17215 combobox.cls += ' roo-select2-container-multi';
17218 var align = this.labelAlign || this.parentLabelAlign();
17220 if (align ==='left' && this.fieldLabel.length) {
17225 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226 tooltip : 'This field is required'
17230 cls : 'control-label col-form-label',
17231 html : this.fieldLabel
17235 cls : 'roo-combobox-wrap ',
17242 var labelCfg = cfg.cn[1];
17243 var contentCfg = cfg.cn[2];
17246 if(this.indicatorpos == 'right'){
17251 cls : 'control-label col-form-label',
17255 html : this.fieldLabel
17259 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260 tooltip : 'This field is required'
17265 cls : "roo-combobox-wrap ",
17273 labelCfg = cfg.cn[0];
17274 contentCfg = cfg.cn[1];
17279 if(this.labelWidth > 12){
17280 labelCfg.style = "width: " + this.labelWidth + 'px';
17283 if(this.labelWidth < 13 && this.labelmd == 0){
17284 this.labelmd = this.labelWidth;
17287 if(this.labellg > 0){
17288 labelCfg.cls += ' col-lg-' + this.labellg;
17289 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17292 if(this.labelmd > 0){
17293 labelCfg.cls += ' col-md-' + this.labelmd;
17294 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17297 if(this.labelsm > 0){
17298 labelCfg.cls += ' col-sm-' + this.labelsm;
17299 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17302 if(this.labelxs > 0){
17303 labelCfg.cls += ' col-xs-' + this.labelxs;
17304 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17308 } else if ( this.fieldLabel.length) {
17312 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313 tooltip : 'This field is required'
17317 cls : 'control-label',
17318 html : this.fieldLabel
17329 if(this.indicatorpos == 'right'){
17333 cls : 'control-label',
17334 html : this.fieldLabel,
17338 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339 tooltip : 'This field is required'
17356 var settings = this;
17358 ['xs','sm','md','lg'].map(function(size){
17359 if (settings[size]) {
17360 cfg.cls += ' col-' + size + '-' + settings[size];
17367 initTouchView : function()
17369 this.renderTouchView();
17371 this.touchViewEl.on('scroll', function(){
17372 this.el.dom.scrollTop = 0;
17375 this.originalValue = this.getValue();
17377 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17379 this.inputEl().on("click", this.showTouchView, this);
17380 if (this.triggerEl) {
17381 this.triggerEl.on("click", this.showTouchView, this);
17385 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17388 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17390 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391 this.store.on('load', this.onTouchViewLoad, this);
17392 this.store.on('loadexception', this.onTouchViewLoadException, this);
17394 if(this.hiddenName){
17396 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17398 this.hiddenField.dom.value =
17399 this.hiddenValue !== undefined ? this.hiddenValue :
17400 this.value !== undefined ? this.value : '';
17402 this.el.dom.removeAttribute('name');
17403 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17407 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17411 if(this.removable && !this.multiple){
17412 var close = this.closeTriggerEl();
17414 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415 close.on('click', this.removeBtnClick, this, close);
17419 * fix the bug in Safari iOS8
17421 this.inputEl().on("focus", function(e){
17422 document.activeElement.blur();
17425 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17432 renderTouchView : function()
17434 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17440 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442 this.touchViewBodyEl.setStyle('overflow', 'auto');
17444 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17447 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17452 showTouchView : function()
17458 this.touchViewHeaderEl.hide();
17460 if(this.modalTitle.length){
17461 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462 this.touchViewHeaderEl.show();
17465 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466 this.touchViewEl.show();
17468 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17470 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17473 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17475 if(this.modalTitle.length){
17476 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17479 this.touchViewBodyEl.setHeight(bodyHeight);
17483 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17485 this.touchViewEl.addClass(['in','show']);
17488 if(this._touchViewMask){
17489 Roo.get(document.body).addClass("x-body-masked");
17490 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17491 this._touchViewMask.setStyle('z-index', 10000);
17492 this._touchViewMask.addClass('show');
17495 this.doTouchViewQuery();
17499 hideTouchView : function()
17501 this.touchViewEl.removeClass(['in','show']);
17505 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17507 this.touchViewEl.setStyle('display', 'none');
17510 if(this._touchViewMask){
17511 this._touchViewMask.removeClass('show');
17512 Roo.get(document.body).removeClass("x-body-masked");
17516 setTouchViewValue : function()
17523 Roo.each(this.tickItems, function(o){
17528 this.hideTouchView();
17531 doTouchViewQuery : function()
17540 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17544 if(!this.alwaysQuery || this.mode == 'local'){
17545 this.onTouchViewLoad();
17552 onTouchViewBeforeLoad : function(combo,opts)
17558 onTouchViewLoad : function()
17560 if(this.store.getCount() < 1){
17561 this.onTouchViewEmptyResults();
17565 this.clearTouchView();
17567 var rawValue = this.getRawValue();
17569 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17571 this.tickItems = [];
17573 this.store.data.each(function(d, rowIndex){
17574 var row = this.touchViewListGroup.createChild(template);
17576 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577 row.addClass(d.data.cls);
17580 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17583 html : d.data[this.displayField]
17586 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17590 row.removeClass('selected');
17591 if(!this.multiple && this.valueField &&
17592 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17595 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596 row.addClass('selected');
17599 if(this.multiple && this.valueField &&
17600 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17604 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605 this.tickItems.push(d.data);
17608 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17612 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17614 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17616 if(this.modalTitle.length){
17617 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17620 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17622 if(this.mobile_restrict_height && listHeight < bodyHeight){
17623 this.touchViewBodyEl.setHeight(listHeight);
17628 if(firstChecked && listHeight > bodyHeight){
17629 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17634 onTouchViewLoadException : function()
17636 this.hideTouchView();
17639 onTouchViewEmptyResults : function()
17641 this.clearTouchView();
17643 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17645 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17649 clearTouchView : function()
17651 this.touchViewListGroup.dom.innerHTML = '';
17654 onTouchViewClick : function(e, el, o)
17656 e.preventDefault();
17659 var rowIndex = o.rowIndex;
17661 var r = this.store.getAt(rowIndex);
17663 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17665 if(!this.multiple){
17666 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667 c.dom.removeAttribute('checked');
17670 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17672 this.setFromData(r.data);
17674 var close = this.closeTriggerEl();
17680 this.hideTouchView();
17682 this.fireEvent('select', this, r, rowIndex);
17687 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17693 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694 this.addItem(r.data);
17695 this.tickItems.push(r.data);
17699 getAutoCreateNativeIOS : function()
17702 cls: 'form-group' //input-group,
17707 cls : 'roo-ios-select'
17711 combobox.name = this.name;
17714 if (this.disabled) {
17715 combobox.disabled = true;
17718 var settings = this;
17720 ['xs','sm','md','lg'].map(function(size){
17721 if (settings[size]) {
17722 cfg.cls += ' col-' + size + '-' + settings[size];
17732 initIOSView : function()
17734 this.store.on('load', this.onIOSViewLoad, this);
17739 onIOSViewLoad : function()
17741 if(this.store.getCount() < 1){
17745 this.clearIOSView();
17747 if(this.allowBlank) {
17749 var default_text = '-- SELECT --';
17751 if(this.placeholder.length){
17752 default_text = this.placeholder;
17755 if(this.emptyTitle.length){
17756 default_text += ' - ' + this.emptyTitle + ' -';
17759 var opt = this.inputEl().createChild({
17762 html : default_text
17766 o[this.valueField] = 0;
17767 o[this.displayField] = default_text;
17769 this.ios_options.push({
17776 this.store.data.each(function(d, rowIndex){
17780 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781 html = d.data[this.displayField];
17786 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787 value = d.data[this.valueField];
17796 if(this.value == d.data[this.valueField]){
17797 option['selected'] = true;
17800 var opt = this.inputEl().createChild(option);
17802 this.ios_options.push({
17809 this.inputEl().on('change', function(){
17810 this.fireEvent('select', this);
17815 clearIOSView: function()
17817 this.inputEl().dom.innerHTML = '';
17819 this.ios_options = [];
17822 setIOSValue: function(v)
17826 if(!this.ios_options){
17830 Roo.each(this.ios_options, function(opts){
17832 opts.el.dom.removeAttribute('selected');
17834 if(opts.data[this.valueField] != v){
17838 opts.el.dom.setAttribute('selected', true);
17844 * @cfg {Boolean} grow
17848 * @cfg {Number} growMin
17852 * @cfg {Number} growMax
17861 Roo.apply(Roo.bootstrap.ComboBox, {
17865 cls: 'modal-header',
17887 cls: 'list-group-item',
17891 cls: 'roo-combobox-list-group-item-value'
17895 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17909 listItemCheckbox : {
17911 cls: 'list-group-item',
17915 cls: 'roo-combobox-list-group-item-value'
17919 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17935 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17940 cls: 'modal-footer',
17948 cls: 'col-xs-6 text-left',
17951 cls: 'btn btn-danger roo-touch-view-cancel',
17957 cls: 'col-xs-6 text-right',
17960 cls: 'btn btn-success roo-touch-view-ok',
17971 Roo.apply(Roo.bootstrap.ComboBox, {
17973 touchViewTemplate : {
17975 cls: 'modal fade roo-combobox-touch-view',
17979 cls: 'modal-dialog',
17980 style : 'position:fixed', // we have to fix position....
17984 cls: 'modal-content',
17986 Roo.bootstrap.ComboBox.header,
17987 Roo.bootstrap.ComboBox.body,
17988 Roo.bootstrap.ComboBox.footer
17997 * Ext JS Library 1.1.1
17998 * Copyright(c) 2006-2007, Ext JS, LLC.
18000 * Originally Released Under LGPL - original licence link has changed is not relivant.
18003 * <script type="text/javascript">
18008 * @extends Roo.util.Observable
18009 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18010 * This class also supports single and multi selection modes. <br>
18011 * Create a data model bound view:
18013 var store = new Roo.data.Store(...);
18015 var view = new Roo.View({
18017 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18019 singleSelect: true,
18020 selectedClass: "ydataview-selected",
18024 // listen for node click?
18025 view.on("click", function(vw, index, node, e){
18026 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18030 dataModel.load("foobar.xml");
18032 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18034 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18037 * Note: old style constructor is still suported (container, template, config)
18040 * Create a new View
18041 * @param {Object} config The config object
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18046 this.parent = false;
18048 if (typeof(depreciated_tpl) == 'undefined') {
18049 // new way.. - universal constructor.
18050 Roo.apply(this, config);
18051 this.el = Roo.get(this.el);
18054 this.el = Roo.get(config);
18055 this.tpl = depreciated_tpl;
18056 Roo.apply(this, depreciated_config);
18058 this.wrapEl = this.el.wrap().wrap();
18059 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18062 if(typeof(this.tpl) == "string"){
18063 this.tpl = new Roo.Template(this.tpl);
18065 // support xtype ctors..
18066 this.tpl = new Roo.factory(this.tpl, Roo);
18070 this.tpl.compile();
18075 * @event beforeclick
18076 * Fires before a click is processed. Returns false to cancel the default action.
18077 * @param {Roo.View} this
18078 * @param {Number} index The index of the target node
18079 * @param {HTMLElement} node The target node
18080 * @param {Roo.EventObject} e The raw event object
18082 "beforeclick" : true,
18085 * Fires when a template node is clicked.
18086 * @param {Roo.View} this
18087 * @param {Number} index The index of the target node
18088 * @param {HTMLElement} node The target node
18089 * @param {Roo.EventObject} e The raw event object
18094 * Fires when a template node is double clicked.
18095 * @param {Roo.View} this
18096 * @param {Number} index The index of the target node
18097 * @param {HTMLElement} node The target node
18098 * @param {Roo.EventObject} e The raw event object
18102 * @event contextmenu
18103 * Fires when a template node is right clicked.
18104 * @param {Roo.View} this
18105 * @param {Number} index The index of the target node
18106 * @param {HTMLElement} node The target node
18107 * @param {Roo.EventObject} e The raw event object
18109 "contextmenu" : true,
18111 * @event selectionchange
18112 * Fires when the selected nodes change.
18113 * @param {Roo.View} this
18114 * @param {Array} selections Array of the selected nodes
18116 "selectionchange" : true,
18119 * @event beforeselect
18120 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121 * @param {Roo.View} this
18122 * @param {HTMLElement} node The node to be selected
18123 * @param {Array} selections Array of currently selected nodes
18125 "beforeselect" : true,
18127 * @event preparedata
18128 * Fires on every row to render, to allow you to change the data.
18129 * @param {Roo.View} this
18130 * @param {Object} data to be rendered (change this)
18132 "preparedata" : true
18140 "click": this.onClick,
18141 "dblclick": this.onDblClick,
18142 "contextmenu": this.onContextMenu,
18146 this.selections = [];
18148 this.cmp = new Roo.CompositeElementLite([]);
18150 this.store = Roo.factory(this.store, Roo.data);
18151 this.setStore(this.store, true);
18154 if ( this.footer && this.footer.xtype) {
18156 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18158 this.footer.dataSource = this.store;
18159 this.footer.container = fctr;
18160 this.footer = Roo.factory(this.footer, Roo);
18161 fctr.insertFirst(this.el);
18163 // this is a bit insane - as the paging toolbar seems to detach the el..
18164 // dom.parentNode.parentNode.parentNode
18165 // they get detached?
18169 Roo.View.superclass.constructor.call(this);
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18177 * @cfg {Roo.data.Store} store Data store to load data from.
18182 * @cfg {String|Roo.Element} el The container element.
18187 * @cfg {String|Roo.Template} tpl The template used by this View
18191 * @cfg {String} dataName the named area of the template to use as the data area
18192 * Works with domtemplates roo-name="name"
18196 * @cfg {String} selectedClass The css class to add to selected nodes
18198 selectedClass : "x-view-selected",
18200 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18205 * @cfg {String} text to display on mask (default Loading)
18209 * @cfg {Boolean} multiSelect Allow multiple selection
18211 multiSelect : false,
18213 * @cfg {Boolean} singleSelect Allow single selection
18215 singleSelect: false,
18218 * @cfg {Boolean} toggleSelect - selecting
18220 toggleSelect : false,
18223 * @cfg {Boolean} tickable - selecting
18228 * Returns the element this view is bound to.
18229 * @return {Roo.Element}
18231 getEl : function(){
18232 return this.wrapEl;
18238 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18240 refresh : function(){
18241 //Roo.log('refresh');
18244 // if we are using something like 'domtemplate', then
18245 // the what gets used is:
18246 // t.applySubtemplate(NAME, data, wrapping data..)
18247 // the outer template then get' applied with
18248 // the store 'extra data'
18249 // and the body get's added to the
18250 // roo-name="data" node?
18251 // <span class='roo-tpl-{name}'></span> ?????
18255 this.clearSelections();
18256 this.el.update("");
18258 var records = this.store.getRange();
18259 if(records.length < 1) {
18261 // is this valid?? = should it render a template??
18263 this.el.update(this.emptyText);
18267 if (this.dataName) {
18268 this.el.update(t.apply(this.store.meta)); //????
18269 el = this.el.child('.roo-tpl-' + this.dataName);
18272 for(var i = 0, len = records.length; i < len; i++){
18273 var data = this.prepareData(records[i].data, i, records[i]);
18274 this.fireEvent("preparedata", this, data, i, records[i]);
18276 var d = Roo.apply({}, data);
18279 Roo.apply(d, {'roo-id' : Roo.id()});
18283 Roo.each(this.parent.item, function(item){
18284 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18287 Roo.apply(d, {'roo-data-checked' : 'checked'});
18291 html[html.length] = Roo.util.Format.trim(
18293 t.applySubtemplate(this.dataName, d, this.store.meta) :
18300 el.update(html.join(""));
18301 this.nodes = el.dom.childNodes;
18302 this.updateIndexes(0);
18307 * Function to override to reformat the data that is sent to
18308 * the template for each node.
18309 * DEPRICATED - use the preparedata event handler.
18310 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311 * a JSON object for an UpdateManager bound view).
18313 prepareData : function(data, index, record)
18315 this.fireEvent("preparedata", this, data, index, record);
18319 onUpdate : function(ds, record){
18320 // Roo.log('on update');
18321 this.clearSelections();
18322 var index = this.store.indexOf(record);
18323 var n = this.nodes[index];
18324 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325 n.parentNode.removeChild(n);
18326 this.updateIndexes(index, index);
18332 onAdd : function(ds, records, index)
18334 //Roo.log(['on Add', ds, records, index] );
18335 this.clearSelections();
18336 if(this.nodes.length == 0){
18340 var n = this.nodes[index];
18341 for(var i = 0, len = records.length; i < len; i++){
18342 var d = this.prepareData(records[i].data, i, records[i]);
18344 this.tpl.insertBefore(n, d);
18347 this.tpl.append(this.el, d);
18350 this.updateIndexes(index);
18353 onRemove : function(ds, record, index){
18354 // Roo.log('onRemove');
18355 this.clearSelections();
18356 var el = this.dataName ?
18357 this.el.child('.roo-tpl-' + this.dataName) :
18360 el.dom.removeChild(this.nodes[index]);
18361 this.updateIndexes(index);
18365 * Refresh an individual node.
18366 * @param {Number} index
18368 refreshNode : function(index){
18369 this.onUpdate(this.store, this.store.getAt(index));
18372 updateIndexes : function(startIndex, endIndex){
18373 var ns = this.nodes;
18374 startIndex = startIndex || 0;
18375 endIndex = endIndex || ns.length - 1;
18376 for(var i = startIndex; i <= endIndex; i++){
18377 ns[i].nodeIndex = i;
18382 * Changes the data store this view uses and refresh the view.
18383 * @param {Store} store
18385 setStore : function(store, initial){
18386 if(!initial && this.store){
18387 this.store.un("datachanged", this.refresh);
18388 this.store.un("add", this.onAdd);
18389 this.store.un("remove", this.onRemove);
18390 this.store.un("update", this.onUpdate);
18391 this.store.un("clear", this.refresh);
18392 this.store.un("beforeload", this.onBeforeLoad);
18393 this.store.un("load", this.onLoad);
18394 this.store.un("loadexception", this.onLoad);
18398 store.on("datachanged", this.refresh, this);
18399 store.on("add", this.onAdd, this);
18400 store.on("remove", this.onRemove, this);
18401 store.on("update", this.onUpdate, this);
18402 store.on("clear", this.refresh, this);
18403 store.on("beforeload", this.onBeforeLoad, this);
18404 store.on("load", this.onLoad, this);
18405 store.on("loadexception", this.onLoad, this);
18413 * onbeforeLoad - masks the loading area.
18416 onBeforeLoad : function(store,opts)
18418 //Roo.log('onBeforeLoad');
18420 this.el.update("");
18422 this.el.mask(this.mask ? this.mask : "Loading" );
18424 onLoad : function ()
18431 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432 * @param {HTMLElement} node
18433 * @return {HTMLElement} The template node
18435 findItemFromChild : function(node){
18436 var el = this.dataName ?
18437 this.el.child('.roo-tpl-' + this.dataName,true) :
18440 if(!node || node.parentNode == el){
18443 var p = node.parentNode;
18444 while(p && p != el){
18445 if(p.parentNode == el){
18454 onClick : function(e){
18455 var item = this.findItemFromChild(e.getTarget());
18457 var index = this.indexOf(item);
18458 if(this.onItemClick(item, index, e) !== false){
18459 this.fireEvent("click", this, index, item, e);
18462 this.clearSelections();
18467 onContextMenu : function(e){
18468 var item = this.findItemFromChild(e.getTarget());
18470 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18475 onDblClick : function(e){
18476 var item = this.findItemFromChild(e.getTarget());
18478 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18482 onItemClick : function(item, index, e)
18484 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18487 if (this.toggleSelect) {
18488 var m = this.isSelected(item) ? 'unselect' : 'select';
18491 _t[m](item, true, false);
18494 if(this.multiSelect || this.singleSelect){
18495 if(this.multiSelect && e.shiftKey && this.lastSelection){
18496 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18498 this.select(item, this.multiSelect && e.ctrlKey);
18499 this.lastSelection = item;
18502 if(!this.tickable){
18503 e.preventDefault();
18511 * Get the number of selected nodes.
18514 getSelectionCount : function(){
18515 return this.selections.length;
18519 * Get the currently selected nodes.
18520 * @return {Array} An array of HTMLElements
18522 getSelectedNodes : function(){
18523 return this.selections;
18527 * Get the indexes of the selected nodes.
18530 getSelectedIndexes : function(){
18531 var indexes = [], s = this.selections;
18532 for(var i = 0, len = s.length; i < len; i++){
18533 indexes.push(s[i].nodeIndex);
18539 * Clear all selections
18540 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18542 clearSelections : function(suppressEvent){
18543 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544 this.cmp.elements = this.selections;
18545 this.cmp.removeClass(this.selectedClass);
18546 this.selections = [];
18547 if(!suppressEvent){
18548 this.fireEvent("selectionchange", this, this.selections);
18554 * Returns true if the passed node is selected
18555 * @param {HTMLElement/Number} node The node or node index
18556 * @return {Boolean}
18558 isSelected : function(node){
18559 var s = this.selections;
18563 node = this.getNode(node);
18564 return s.indexOf(node) !== -1;
18569 * @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
18570 * @param {Boolean} keepExisting (optional) true to keep existing selections
18571 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18573 select : function(nodeInfo, keepExisting, suppressEvent){
18574 if(nodeInfo instanceof Array){
18576 this.clearSelections(true);
18578 for(var i = 0, len = nodeInfo.length; i < len; i++){
18579 this.select(nodeInfo[i], true, true);
18583 var node = this.getNode(nodeInfo);
18584 if(!node || this.isSelected(node)){
18585 return; // already selected.
18588 this.clearSelections(true);
18591 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592 Roo.fly(node).addClass(this.selectedClass);
18593 this.selections.push(node);
18594 if(!suppressEvent){
18595 this.fireEvent("selectionchange", this, this.selections);
18603 * @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
18604 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18607 unselect : function(nodeInfo, keepExisting, suppressEvent)
18609 if(nodeInfo instanceof Array){
18610 Roo.each(this.selections, function(s) {
18611 this.unselect(s, nodeInfo);
18615 var node = this.getNode(nodeInfo);
18616 if(!node || !this.isSelected(node)){
18617 //Roo.log("not selected");
18618 return; // not selected.
18622 Roo.each(this.selections, function(s) {
18624 Roo.fly(node).removeClass(this.selectedClass);
18631 this.selections= ns;
18632 this.fireEvent("selectionchange", this, this.selections);
18636 * Gets a template node.
18637 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638 * @return {HTMLElement} The node or null if it wasn't found
18640 getNode : function(nodeInfo){
18641 if(typeof nodeInfo == "string"){
18642 return document.getElementById(nodeInfo);
18643 }else if(typeof nodeInfo == "number"){
18644 return this.nodes[nodeInfo];
18650 * Gets a range template nodes.
18651 * @param {Number} startIndex
18652 * @param {Number} endIndex
18653 * @return {Array} An array of nodes
18655 getNodes : function(start, end){
18656 var ns = this.nodes;
18657 start = start || 0;
18658 end = typeof end == "undefined" ? ns.length - 1 : end;
18661 for(var i = start; i <= end; i++){
18665 for(var i = start; i >= end; i--){
18673 * Finds the index of the passed node
18674 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675 * @return {Number} The index of the node or -1
18677 indexOf : function(node){
18678 node = this.getNode(node);
18679 if(typeof node.nodeIndex == "number"){
18680 return node.nodeIndex;
18682 var ns = this.nodes;
18683 for(var i = 0, len = ns.length; i < len; i++){
18694 * based on jquery fullcalendar
18698 Roo.bootstrap = Roo.bootstrap || {};
18700 * @class Roo.bootstrap.Calendar
18701 * @extends Roo.bootstrap.Component
18702 * Bootstrap Calendar class
18703 * @cfg {Boolean} loadMask (true|false) default false
18704 * @cfg {Object} header generate the user specific header of the calendar, default false
18707 * Create a new Container
18708 * @param {Object} config The config object
18713 Roo.bootstrap.Calendar = function(config){
18714 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18718 * Fires when a date is selected
18719 * @param {DatePicker} this
18720 * @param {Date} date The selected date
18724 * @event monthchange
18725 * Fires when the displayed month changes
18726 * @param {DatePicker} this
18727 * @param {Date} date The selected month
18729 'monthchange': true,
18731 * @event evententer
18732 * Fires when mouse over an event
18733 * @param {Calendar} this
18734 * @param {event} Event
18736 'evententer': true,
18738 * @event eventleave
18739 * Fires when the mouse leaves an
18740 * @param {Calendar} this
18743 'eventleave': true,
18745 * @event eventclick
18746 * Fires when the mouse click an
18747 * @param {Calendar} this
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18759 * @cfg {Number} startDay
18760 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18768 getAutoCreate : function(){
18771 var fc_button = function(name, corner, style, content ) {
18772 return Roo.apply({},{
18774 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18776 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18779 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18790 style : 'width:100%',
18797 cls : 'fc-header-left',
18799 fc_button('prev', 'left', 'arrow', '‹' ),
18800 fc_button('next', 'right', 'arrow', '›' ),
18801 { tag: 'span', cls: 'fc-header-space' },
18802 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18810 cls : 'fc-header-center',
18814 cls: 'fc-header-title',
18817 html : 'month / year'
18825 cls : 'fc-header-right',
18827 /* fc_button('month', 'left', '', 'month' ),
18828 fc_button('week', '', '', 'week' ),
18829 fc_button('day', 'right', '', 'day' )
18841 header = this.header;
18844 var cal_heads = function() {
18846 // fixme - handle this.
18848 for (var i =0; i < Date.dayNames.length; i++) {
18849 var d = Date.dayNames[i];
18852 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853 html : d.substring(0,3)
18857 ret[0].cls += ' fc-first';
18858 ret[6].cls += ' fc-last';
18861 var cal_cell = function(n) {
18864 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18869 cls: 'fc-day-number',
18873 cls: 'fc-day-content',
18877 style: 'position: relative;' // height: 17px;
18889 var cal_rows = function() {
18892 for (var r = 0; r < 6; r++) {
18899 for (var i =0; i < Date.dayNames.length; i++) {
18900 var d = Date.dayNames[i];
18901 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18904 row.cn[0].cls+=' fc-first';
18905 row.cn[0].cn[0].style = 'min-height:90px';
18906 row.cn[6].cls+=' fc-last';
18910 ret[0].cls += ' fc-first';
18911 ret[4].cls += ' fc-prev-last';
18912 ret[5].cls += ' fc-last';
18919 cls: 'fc-border-separate',
18920 style : 'width:100%',
18928 cls : 'fc-first fc-last',
18946 cls : 'fc-content',
18947 style : "position: relative;",
18950 cls : 'fc-view fc-view-month fc-grid',
18951 style : 'position: relative',
18952 unselectable : 'on',
18955 cls : 'fc-event-container',
18956 style : 'position:absolute;z-index:8;top:0;left:0;'
18974 initEvents : function()
18977 throw "can not find store for calendar";
18983 style: "text-align:center",
18987 style: "background-color:white;width:50%;margin:250 auto",
18991 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19002 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19004 var size = this.el.select('.fc-content', true).first().getSize();
19005 this.maskEl.setSize(size.width, size.height);
19006 this.maskEl.enableDisplayMode("block");
19007 if(!this.loadMask){
19008 this.maskEl.hide();
19011 this.store = Roo.factory(this.store, Roo.data);
19012 this.store.on('load', this.onLoad, this);
19013 this.store.on('beforeload', this.onBeforeLoad, this);
19017 this.cells = this.el.select('.fc-day',true);
19018 //Roo.log(this.cells);
19019 this.textNodes = this.el.query('.fc-day-number');
19020 this.cells.addClassOnOver('fc-state-hover');
19022 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19027 this.on('monthchange', this.onMonthChange, this);
19029 this.update(new Date().clearTime());
19032 resize : function() {
19033 var sz = this.el.getSize();
19035 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036 this.el.select('.fc-day-content div',true).setHeight(34);
19041 showPrevMonth : function(e){
19042 this.update(this.activeDate.add("mo", -1));
19044 showToday : function(e){
19045 this.update(new Date().clearTime());
19048 showNextMonth : function(e){
19049 this.update(this.activeDate.add("mo", 1));
19053 showPrevYear : function(){
19054 this.update(this.activeDate.add("y", -1));
19058 showNextYear : function(){
19059 this.update(this.activeDate.add("y", 1));
19064 update : function(date)
19066 var vd = this.activeDate;
19067 this.activeDate = date;
19068 // if(vd && this.el){
19069 // var t = date.getTime();
19070 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 // Roo.log('using add remove');
19073 // this.fireEvent('monthchange', this, date);
19075 // this.cells.removeClass("fc-state-highlight");
19076 // this.cells.each(function(c){
19077 // if(c.dateValue == t){
19078 // c.addClass("fc-state-highlight");
19079 // setTimeout(function(){
19080 // try{c.dom.firstChild.focus();}catch(e){}
19090 var days = date.getDaysInMonth();
19092 var firstOfMonth = date.getFirstDateOfMonth();
19093 var startingPos = firstOfMonth.getDay()-this.startDay;
19095 if(startingPos < this.startDay){
19099 var pm = date.add(Date.MONTH, -1);
19100 var prevStart = pm.getDaysInMonth()-startingPos;
19102 this.cells = this.el.select('.fc-day',true);
19103 this.textNodes = this.el.query('.fc-day-number');
19104 this.cells.addClassOnOver('fc-state-hover');
19106 var cells = this.cells.elements;
19107 var textEls = this.textNodes;
19109 Roo.each(cells, function(cell){
19110 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19113 days += startingPos;
19115 // convert everything to numbers so it's fast
19116 var day = 86400000;
19117 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19120 //Roo.log(prevStart);
19122 var today = new Date().clearTime().getTime();
19123 var sel = date.clearTime().getTime();
19124 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126 var ddMatch = this.disabledDatesRE;
19127 var ddText = this.disabledDatesText;
19128 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129 var ddaysText = this.disabledDaysText;
19130 var format = this.format;
19132 var setCellClass = function(cal, cell){
19136 //Roo.log('set Cell Class');
19138 var t = d.getTime();
19142 cell.dateValue = t;
19144 cell.className += " fc-today";
19145 cell.className += " fc-state-highlight";
19146 cell.title = cal.todayText;
19149 // disable highlight in other month..
19150 //cell.className += " fc-state-highlight";
19155 cell.className = " fc-state-disabled";
19156 cell.title = cal.minText;
19160 cell.className = " fc-state-disabled";
19161 cell.title = cal.maxText;
19165 if(ddays.indexOf(d.getDay()) != -1){
19166 cell.title = ddaysText;
19167 cell.className = " fc-state-disabled";
19170 if(ddMatch && format){
19171 var fvalue = d.dateFormat(format);
19172 if(ddMatch.test(fvalue)){
19173 cell.title = ddText.replace("%0", fvalue);
19174 cell.className = " fc-state-disabled";
19178 if (!cell.initialClassName) {
19179 cell.initialClassName = cell.dom.className;
19182 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19187 for(; i < startingPos; i++) {
19188 textEls[i].innerHTML = (++prevStart);
19189 d.setDate(d.getDate()+1);
19191 cells[i].className = "fc-past fc-other-month";
19192 setCellClass(this, cells[i]);
19197 for(; i < days; i++){
19198 intDay = i - startingPos + 1;
19199 textEls[i].innerHTML = (intDay);
19200 d.setDate(d.getDate()+1);
19202 cells[i].className = ''; // "x-date-active";
19203 setCellClass(this, cells[i]);
19207 for(; i < 42; i++) {
19208 textEls[i].innerHTML = (++extraDays);
19209 d.setDate(d.getDate()+1);
19211 cells[i].className = "fc-future fc-other-month";
19212 setCellClass(this, cells[i]);
19215 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19217 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19219 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19222 if(totalRows != 6){
19223 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19227 this.fireEvent('monthchange', this, date);
19231 if(!this.internalRender){
19232 var main = this.el.dom.firstChild;
19233 var w = main.offsetWidth;
19234 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235 Roo.fly(main).setWidth(w);
19236 this.internalRender = true;
19237 // opera does not respect the auto grow header center column
19238 // then, after it gets a width opera refuses to recalculate
19239 // without a second pass
19240 if(Roo.isOpera && !this.secondPass){
19241 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242 this.secondPass = true;
19243 this.update.defer(10, this, [date]);
19250 findCell : function(dt) {
19251 dt = dt.clearTime().getTime();
19253 this.cells.each(function(c){
19254 //Roo.log("check " +c.dateValue + '?=' + dt);
19255 if(c.dateValue == dt){
19265 findCells : function(ev) {
19266 var s = ev.start.clone().clearTime().getTime();
19268 var e= ev.end.clone().clearTime().getTime();
19271 this.cells.each(function(c){
19272 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19274 if(c.dateValue > e){
19277 if(c.dateValue < s){
19286 // findBestRow: function(cells)
19290 // for (var i =0 ; i < cells.length;i++) {
19291 // ret = Math.max(cells[i].rows || 0,ret);
19298 addItem : function(ev)
19300 // look for vertical location slot in
19301 var cells = this.findCells(ev);
19303 // ev.row = this.findBestRow(cells);
19305 // work out the location.
19309 for(var i =0; i < cells.length; i++) {
19311 cells[i].row = cells[0].row;
19314 cells[i].row = cells[i].row + 1;
19324 if (crow.start.getY() == cells[i].getY()) {
19326 crow.end = cells[i];
19343 cells[0].events.push(ev);
19345 this.calevents.push(ev);
19348 clearEvents: function() {
19350 if(!this.calevents){
19354 Roo.each(this.cells.elements, function(c){
19360 Roo.each(this.calevents, function(e) {
19361 Roo.each(e.els, function(el) {
19362 el.un('mouseenter' ,this.onEventEnter, this);
19363 el.un('mouseleave' ,this.onEventLeave, this);
19368 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19374 renderEvents: function()
19378 this.cells.each(function(c) {
19387 if(c.row != c.events.length){
19388 r = 4 - (4 - (c.row - c.events.length));
19391 c.events = ev.slice(0, r);
19392 c.more = ev.slice(r);
19394 if(c.more.length && c.more.length == 1){
19395 c.events.push(c.more.pop());
19398 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19402 this.cells.each(function(c) {
19404 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19407 for (var e = 0; e < c.events.length; e++){
19408 var ev = c.events[e];
19409 var rows = ev.rows;
19411 for(var i = 0; i < rows.length; i++) {
19413 // how many rows should it span..
19416 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19419 unselectable : "on",
19422 cls: 'fc-event-inner',
19426 // cls: 'fc-event-time',
19427 // html : cells.length > 1 ? '' : ev.time
19431 cls: 'fc-event-title',
19432 html : String.format('{0}', ev.title)
19439 cls: 'ui-resizable-handle ui-resizable-e',
19440 html : '  '
19447 cfg.cls += ' fc-event-start';
19449 if ((i+1) == rows.length) {
19450 cfg.cls += ' fc-event-end';
19453 var ctr = _this.el.select('.fc-event-container',true).first();
19454 var cg = ctr.createChild(cfg);
19456 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19459 var r = (c.more.length) ? 1 : 0;
19460 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19461 cg.setWidth(ebox.right - sbox.x -2);
19463 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465 cg.on('click', _this.onEventClick, _this, ev);
19476 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477 style : 'position: absolute',
19478 unselectable : "on",
19481 cls: 'fc-event-inner',
19485 cls: 'fc-event-title',
19493 cls: 'ui-resizable-handle ui-resizable-e',
19494 html : '  '
19500 var ctr = _this.el.select('.fc-event-container',true).first();
19501 var cg = ctr.createChild(cfg);
19503 var sbox = c.select('.fc-day-content',true).first().getBox();
19504 var ebox = c.select('.fc-day-content',true).first().getBox();
19506 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19507 cg.setWidth(ebox.right - sbox.x -2);
19509 cg.on('click', _this.onMoreEventClick, _this, c.more);
19519 onEventEnter: function (e, el,event,d) {
19520 this.fireEvent('evententer', this, el, event);
19523 onEventLeave: function (e, el,event,d) {
19524 this.fireEvent('eventleave', this, el, event);
19527 onEventClick: function (e, el,event,d) {
19528 this.fireEvent('eventclick', this, el, event);
19531 onMonthChange: function () {
19535 onMoreEventClick: function(e, el, more)
19539 this.calpopover.placement = 'right';
19540 this.calpopover.setTitle('More');
19542 this.calpopover.setContent('');
19544 var ctr = this.calpopover.el.select('.popover-content', true).first();
19546 Roo.each(more, function(m){
19548 cls : 'fc-event-hori fc-event-draggable',
19551 var cg = ctr.createChild(cfg);
19553 cg.on('click', _this.onEventClick, _this, m);
19556 this.calpopover.show(el);
19561 onLoad: function ()
19563 this.calevents = [];
19566 if(this.store.getCount() > 0){
19567 this.store.data.each(function(d){
19570 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572 time : d.data.start_time,
19573 title : d.data.title,
19574 description : d.data.description,
19575 venue : d.data.venue
19580 this.renderEvents();
19582 if(this.calevents.length && this.loadMask){
19583 this.maskEl.hide();
19587 onBeforeLoad: function()
19589 this.clearEvents();
19591 this.maskEl.show();
19605 * @class Roo.bootstrap.Popover
19606 * @extends Roo.bootstrap.Component
19607 * Bootstrap Popover class
19608 * @cfg {String} html contents of the popover (or false to use children..)
19609 * @cfg {String} title of popover (or false to hide)
19610 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611 * @cfg {String} trigger click || hover (or false to trigger manually)
19612 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614 * - if false and it has a 'parent' then it will be automatically added to that element
19615 * - if string - Roo.get will be called
19616 * @cfg {Number} delay - delay before showing
19619 * Create a new Popover
19620 * @param {Object} config The config object
19623 Roo.bootstrap.Popover = function(config){
19624 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19630 * After the popover show
19632 * @param {Roo.bootstrap.Popover} this
19637 * After the popover hide
19639 * @param {Roo.bootstrap.Popover} this
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19650 placement : 'right',
19651 trigger : 'hover', // hover
19657 can_build_overlaid : false,
19659 maskEl : false, // the mask element
19662 alignEl : false, // when show is called with an element - this get's stored.
19664 getChildContainer : function()
19666 return this.contentEl;
19669 getPopoverHeader : function()
19671 this.title = true; // flag not to hide it..
19672 this.headerEl.addClass('p-0');
19673 return this.headerEl
19677 getAutoCreate : function(){
19680 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681 style: 'display:block',
19687 cls : 'popover-inner ',
19691 cls: 'popover-title popover-header',
19692 html : this.title === false ? '' : this.title
19695 cls : 'popover-content popover-body ' + (this.cls || ''),
19696 html : this.html || ''
19707 * @param {string} the title
19709 setTitle: function(str)
19713 this.headerEl.dom.innerHTML = str;
19718 * @param {string} the body content
19720 setContent: function(str)
19723 if (this.contentEl) {
19724 this.contentEl.dom.innerHTML = str;
19728 // as it get's added to the bottom of the page.
19729 onRender : function(ct, position)
19731 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19736 var cfg = Roo.apply({}, this.getAutoCreate());
19740 cfg.cls += ' ' + this.cls;
19743 cfg.style = this.style;
19745 //Roo.log("adding to ");
19746 this.el = Roo.get(document.body).createChild(cfg, position);
19747 // Roo.log(this.el);
19750 this.contentEl = this.el.select('.popover-content',true).first();
19751 this.headerEl = this.el.select('.popover-title',true).first();
19754 if(typeof(this.items) != 'undefined'){
19755 var items = this.items;
19758 for(var i =0;i < items.length;i++) {
19759 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19763 this.items = nitems;
19765 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19773 resizeMask : function()
19775 this.maskEl.setSize(
19776 Roo.lib.Dom.getViewWidth(true),
19777 Roo.lib.Dom.getViewHeight(true)
19781 initEvents : function()
19785 Roo.bootstrap.Popover.register(this);
19788 this.arrowEl = this.el.select('.arrow',true).first();
19789 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790 this.el.enableDisplayMode('block');
19794 if (this.over === false && !this.parent()) {
19797 if (this.triggers === false) {
19802 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803 var triggers = this.trigger ? this.trigger.split(' ') : [];
19804 Roo.each(triggers, function(trigger) {
19806 if (trigger == 'click') {
19807 on_el.on('click', this.toggle, this);
19808 } else if (trigger != 'manual') {
19809 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19812 on_el.on(eventIn ,this.enter, this);
19813 on_el.on(eventOut, this.leave, this);
19823 toggle : function () {
19824 this.hoverState == 'in' ? this.leave() : this.enter();
19827 enter : function () {
19829 clearTimeout(this.timeout);
19831 this.hoverState = 'in';
19833 if (!this.delay || !this.delay.show) {
19838 this.timeout = setTimeout(function () {
19839 if (_t.hoverState == 'in') {
19842 }, this.delay.show)
19845 leave : function() {
19846 clearTimeout(this.timeout);
19848 this.hoverState = 'out';
19850 if (!this.delay || !this.delay.hide) {
19855 this.timeout = setTimeout(function () {
19856 if (_t.hoverState == 'out') {
19859 }, this.delay.hide)
19863 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864 * @param {string} (left|right|top|bottom) position
19866 show : function (on_el, placement)
19868 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19869 on_el = on_el || false; // default to false
19872 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19873 on_el = this.parent().el;
19874 } else if (this.over) {
19875 Roo.get(this.over);
19881 this.render(document.body);
19885 this.el.removeClass([
19886 'fade','top','bottom', 'left', 'right','in',
19887 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19890 if (this.title === false) {
19891 this.headerEl.hide();
19896 this.el.dom.style.display = 'block';
19899 this.el.addClass(placement + ' roo-popover-' + placement);
19902 this.updatePosition();
19905 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19906 var es = this.el.getSize();
19907 var x = Roo.lib.Dom.getViewWidth()/2;
19908 var y = Roo.lib.Dom.getViewHeight()/2;
19909 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19914 //var arrow = this.el.select('.arrow',true).first();
19915 //arrow.set(align[2],
19917 this.el.addClass('in');
19921 this.hoverState = 'in';
19924 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19925 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19926 this.maskEl.dom.style.display = 'block';
19927 this.maskEl.addClass('show');
19929 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19931 this.fireEvent('show', this);
19935 * fire this manually after loading a grid in the table for example
19936 * @param {string} (left|right|top|bottom) where to try and put it
19937 * @param {Boolean} try and move it if we cant get right position.
19939 updatePosition : function(placement, try_move)
19941 this.el.removeClass([
19942 'fade','top','bottom', 'left', 'right','in',
19943 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19945 this.el.addClass(placement + ' roo-popover-' + placement);
19947 if (!this.alignEl ) {
19951 switch (placement) {
19953 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19954 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19955 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19956 //normal display... or moved up/down.
19957 this.setXY(offset);
19958 var xy = this.alignEl.getAnchorXY('tr', false);
19960 this.arrowEl.setXY(xy);
19963 // continue through...
19968 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19969 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19970 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19971 //normal display... or moved up/down.
19972 this.setXY(offset);
19973 var xy = this.alignEl.getAnchorXY('tl', false);
19974 xy[0]+=2;xy[1]+=5; // << fix me
19975 this.arrowEl.setXY(xy);
19979 return this.updatePosition('right', false);
19982 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,-10]);
19983 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,-10]);
19984 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19985 //normal display... or moved up/down.
19986 this.setXY(offset);
19987 var xy = this.alignEl.getAnchorXY('b', false);
19988 xy[0]+=2;xy[1]+=5; // << fix me
19989 this.arrowEl.setXY(xy);
19996 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,10]);
19997 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,10]);
19998 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19999 //normal display... or moved up/down.
20000 this.setXY(offset);
20001 var xy = this.alignEl.getAnchorXY('t', false);
20002 xy[0]+=2;xy[1]+=5; // << fix me
20003 this.arrowEl.setXY(xy);
20007 return this.updatePosition('top', false);
20018 this.el.setXY([0,0]);
20019 this.el.removeClass('in');
20021 this.hoverState = null;
20022 this.maskEl.hide(); // always..
20023 this.fireEvent('hide', this);
20029 Roo.apply(Roo.bootstrap.Popover, {
20032 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20033 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20034 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20035 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20040 clickHander : false,
20043 onMouseDown : function(e)
20045 if (!e.getTarget(".roo-popover")) {
20053 register : function(popup)
20055 if (!Roo.bootstrap.Popover.clickHandler) {
20056 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20058 // hide other popups.
20060 this.popups.push(popup);
20062 hideAll : function()
20064 this.popups.forEach(function(p) {
20072 * Card header - holder for the card header elements.
20077 * @class Roo.bootstrap.PopoverNav
20078 * @extends Roo.bootstrap.NavGroup
20079 * Bootstrap Popover header navigation class
20081 * Create a new Popover Header Navigation
20082 * @param {Object} config The config object
20085 Roo.bootstrap.PopoverNav = function(config){
20086 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20089 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20092 container_method : 'getPopoverHeader'
20110 * @class Roo.bootstrap.Progress
20111 * @extends Roo.bootstrap.Component
20112 * Bootstrap Progress class
20113 * @cfg {Boolean} striped striped of the progress bar
20114 * @cfg {Boolean} active animated of the progress bar
20118 * Create a new Progress
20119 * @param {Object} config The config object
20122 Roo.bootstrap.Progress = function(config){
20123 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20126 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20131 getAutoCreate : function(){
20139 cfg.cls += ' progress-striped';
20143 cfg.cls += ' active';
20162 * @class Roo.bootstrap.ProgressBar
20163 * @extends Roo.bootstrap.Component
20164 * Bootstrap ProgressBar class
20165 * @cfg {Number} aria_valuenow aria-value now
20166 * @cfg {Number} aria_valuemin aria-value min
20167 * @cfg {Number} aria_valuemax aria-value max
20168 * @cfg {String} label label for the progress bar
20169 * @cfg {String} panel (success | info | warning | danger )
20170 * @cfg {String} role role of the progress bar
20171 * @cfg {String} sr_only text
20175 * Create a new ProgressBar
20176 * @param {Object} config The config object
20179 Roo.bootstrap.ProgressBar = function(config){
20180 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20183 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20187 aria_valuemax : 100,
20193 getAutoCreate : function()
20198 cls: 'progress-bar',
20199 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20211 cfg.role = this.role;
20214 if(this.aria_valuenow){
20215 cfg['aria-valuenow'] = this.aria_valuenow;
20218 if(this.aria_valuemin){
20219 cfg['aria-valuemin'] = this.aria_valuemin;
20222 if(this.aria_valuemax){
20223 cfg['aria-valuemax'] = this.aria_valuemax;
20226 if(this.label && !this.sr_only){
20227 cfg.html = this.label;
20231 cfg.cls += ' progress-bar-' + this.panel;
20237 update : function(aria_valuenow)
20239 this.aria_valuenow = aria_valuenow;
20241 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20256 * @class Roo.bootstrap.TabGroup
20257 * @extends Roo.bootstrap.Column
20258 * Bootstrap Column class
20259 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20260 * @cfg {Boolean} carousel true to make the group behave like a carousel
20261 * @cfg {Boolean} bullets show bullets for the panels
20262 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20263 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20264 * @cfg {Boolean} showarrow (true|false) show arrow default true
20267 * Create a new TabGroup
20268 * @param {Object} config The config object
20271 Roo.bootstrap.TabGroup = function(config){
20272 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20274 this.navId = Roo.id();
20277 Roo.bootstrap.TabGroup.register(this);
20281 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20284 transition : false,
20289 slideOnTouch : false,
20292 getAutoCreate : function()
20294 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20296 cfg.cls += ' tab-content';
20298 if (this.carousel) {
20299 cfg.cls += ' carousel slide';
20302 cls : 'carousel-inner',
20306 if(this.bullets && !Roo.isTouch){
20309 cls : 'carousel-bullets',
20313 if(this.bullets_cls){
20314 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20321 cfg.cn[0].cn.push(bullets);
20324 if(this.showarrow){
20325 cfg.cn[0].cn.push({
20327 class : 'carousel-arrow',
20331 class : 'carousel-prev',
20335 class : 'fa fa-chevron-left'
20341 class : 'carousel-next',
20345 class : 'fa fa-chevron-right'
20358 initEvents: function()
20360 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20361 // this.el.on("touchstart", this.onTouchStart, this);
20364 if(this.autoslide){
20367 this.slideFn = window.setInterval(function() {
20368 _this.showPanelNext();
20372 if(this.showarrow){
20373 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20374 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20380 // onTouchStart : function(e, el, o)
20382 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20386 // this.showPanelNext();
20390 getChildContainer : function()
20392 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20396 * register a Navigation item
20397 * @param {Roo.bootstrap.NavItem} the navitem to add
20399 register : function(item)
20401 this.tabs.push( item);
20402 item.navId = this.navId; // not really needed..
20407 getActivePanel : function()
20410 Roo.each(this.tabs, function(t) {
20420 getPanelByName : function(n)
20423 Roo.each(this.tabs, function(t) {
20424 if (t.tabId == n) {
20432 indexOfPanel : function(p)
20435 Roo.each(this.tabs, function(t,i) {
20436 if (t.tabId == p.tabId) {
20445 * show a specific panel
20446 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20447 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20449 showPanel : function (pan)
20451 if(this.transition || typeof(pan) == 'undefined'){
20452 Roo.log("waiting for the transitionend");
20456 if (typeof(pan) == 'number') {
20457 pan = this.tabs[pan];
20460 if (typeof(pan) == 'string') {
20461 pan = this.getPanelByName(pan);
20464 var cur = this.getActivePanel();
20467 Roo.log('pan or acitve pan is undefined');
20471 if (pan.tabId == this.getActivePanel().tabId) {
20475 if (false === cur.fireEvent('beforedeactivate')) {
20479 if(this.bullets > 0 && !Roo.isTouch){
20480 this.setActiveBullet(this.indexOfPanel(pan));
20483 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20485 //class="carousel-item carousel-item-next carousel-item-left"
20487 this.transition = true;
20488 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20489 var lr = dir == 'next' ? 'left' : 'right';
20490 pan.el.addClass(dir); // or prev
20491 pan.el.addClass('carousel-item-' + dir); // or prev
20492 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20493 cur.el.addClass(lr); // or right
20494 pan.el.addClass(lr);
20495 cur.el.addClass('carousel-item-' +lr); // or right
20496 pan.el.addClass('carousel-item-' +lr);
20500 cur.el.on('transitionend', function() {
20501 Roo.log("trans end?");
20503 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20504 pan.setActive(true);
20506 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20507 cur.setActive(false);
20509 _this.transition = false;
20511 }, this, { single: true } );
20516 cur.setActive(false);
20517 pan.setActive(true);
20522 showPanelNext : function()
20524 var i = this.indexOfPanel(this.getActivePanel());
20526 if (i >= this.tabs.length - 1 && !this.autoslide) {
20530 if (i >= this.tabs.length - 1 && this.autoslide) {
20534 this.showPanel(this.tabs[i+1]);
20537 showPanelPrev : function()
20539 var i = this.indexOfPanel(this.getActivePanel());
20541 if (i < 1 && !this.autoslide) {
20545 if (i < 1 && this.autoslide) {
20546 i = this.tabs.length;
20549 this.showPanel(this.tabs[i-1]);
20553 addBullet: function()
20555 if(!this.bullets || Roo.isTouch){
20558 var ctr = this.el.select('.carousel-bullets',true).first();
20559 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20560 var bullet = ctr.createChild({
20561 cls : 'bullet bullet-' + i
20562 },ctr.dom.lastChild);
20567 bullet.on('click', (function(e, el, o, ii, t){
20569 e.preventDefault();
20571 this.showPanel(ii);
20573 if(this.autoslide && this.slideFn){
20574 clearInterval(this.slideFn);
20575 this.slideFn = window.setInterval(function() {
20576 _this.showPanelNext();
20580 }).createDelegate(this, [i, bullet], true));
20585 setActiveBullet : function(i)
20591 Roo.each(this.el.select('.bullet', true).elements, function(el){
20592 el.removeClass('selected');
20595 var bullet = this.el.select('.bullet-' + i, true).first();
20601 bullet.addClass('selected');
20612 Roo.apply(Roo.bootstrap.TabGroup, {
20616 * register a Navigation Group
20617 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20619 register : function(navgrp)
20621 this.groups[navgrp.navId] = navgrp;
20625 * fetch a Navigation Group based on the navigation ID
20626 * if one does not exist , it will get created.
20627 * @param {string} the navgroup to add
20628 * @returns {Roo.bootstrap.NavGroup} the navgroup
20630 get: function(navId) {
20631 if (typeof(this.groups[navId]) == 'undefined') {
20632 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20634 return this.groups[navId] ;
20649 * @class Roo.bootstrap.TabPanel
20650 * @extends Roo.bootstrap.Component
20651 * Bootstrap TabPanel class
20652 * @cfg {Boolean} active panel active
20653 * @cfg {String} html panel content
20654 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20655 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20656 * @cfg {String} href click to link..
20657 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20661 * Create a new TabPanel
20662 * @param {Object} config The config object
20665 Roo.bootstrap.TabPanel = function(config){
20666 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20670 * Fires when the active status changes
20671 * @param {Roo.bootstrap.TabPanel} this
20672 * @param {Boolean} state the new state
20677 * @event beforedeactivate
20678 * Fires before a tab is de-activated - can be used to do validation on a form.
20679 * @param {Roo.bootstrap.TabPanel} this
20680 * @return {Boolean} false if there is an error
20683 'beforedeactivate': true
20686 this.tabId = this.tabId || Roo.id();
20690 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20697 touchSlide : false,
20698 getAutoCreate : function(){
20703 // item is needed for carousel - not sure if it has any effect otherwise
20704 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20705 html: this.html || ''
20709 cfg.cls += ' active';
20713 cfg.tabId = this.tabId;
20721 initEvents: function()
20723 var p = this.parent();
20725 this.navId = this.navId || p.navId;
20727 if (typeof(this.navId) != 'undefined') {
20728 // not really needed.. but just in case.. parent should be a NavGroup.
20729 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20733 var i = tg.tabs.length - 1;
20735 if(this.active && tg.bullets > 0 && i < tg.bullets){
20736 tg.setActiveBullet(i);
20740 this.el.on('click', this.onClick, this);
20742 if(Roo.isTouch && this.touchSlide){
20743 this.el.on("touchstart", this.onTouchStart, this);
20744 this.el.on("touchmove", this.onTouchMove, this);
20745 this.el.on("touchend", this.onTouchEnd, this);
20750 onRender : function(ct, position)
20752 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20755 setActive : function(state)
20757 Roo.log("panel - set active " + this.tabId + "=" + state);
20759 this.active = state;
20761 this.el.removeClass('active');
20763 } else if (!this.el.hasClass('active')) {
20764 this.el.addClass('active');
20767 this.fireEvent('changed', this, state);
20770 onClick : function(e)
20772 e.preventDefault();
20774 if(!this.href.length){
20778 window.location.href = this.href;
20787 onTouchStart : function(e)
20789 this.swiping = false;
20791 this.startX = e.browserEvent.touches[0].clientX;
20792 this.startY = e.browserEvent.touches[0].clientY;
20795 onTouchMove : function(e)
20797 this.swiping = true;
20799 this.endX = e.browserEvent.touches[0].clientX;
20800 this.endY = e.browserEvent.touches[0].clientY;
20803 onTouchEnd : function(e)
20810 var tabGroup = this.parent();
20812 if(this.endX > this.startX){ // swiping right
20813 tabGroup.showPanelPrev();
20817 if(this.startX > this.endX){ // swiping left
20818 tabGroup.showPanelNext();
20837 * @class Roo.bootstrap.DateField
20838 * @extends Roo.bootstrap.Input
20839 * Bootstrap DateField class
20840 * @cfg {Number} weekStart default 0
20841 * @cfg {String} viewMode default empty, (months|years)
20842 * @cfg {String} minViewMode default empty, (months|years)
20843 * @cfg {Number} startDate default -Infinity
20844 * @cfg {Number} endDate default Infinity
20845 * @cfg {Boolean} todayHighlight default false
20846 * @cfg {Boolean} todayBtn default false
20847 * @cfg {Boolean} calendarWeeks default false
20848 * @cfg {Object} daysOfWeekDisabled default empty
20849 * @cfg {Boolean} singleMode default false (true | false)
20851 * @cfg {Boolean} keyboardNavigation default true
20852 * @cfg {String} language default en
20855 * Create a new DateField
20856 * @param {Object} config The config object
20859 Roo.bootstrap.DateField = function(config){
20860 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20864 * Fires when this field show.
20865 * @param {Roo.bootstrap.DateField} this
20866 * @param {Mixed} date The date value
20871 * Fires when this field hide.
20872 * @param {Roo.bootstrap.DateField} this
20873 * @param {Mixed} date The date value
20878 * Fires when select a date.
20879 * @param {Roo.bootstrap.DateField} this
20880 * @param {Mixed} date The date value
20884 * @event beforeselect
20885 * Fires when before select a date.
20886 * @param {Roo.bootstrap.DateField} this
20887 * @param {Mixed} date The date value
20889 beforeselect : true
20893 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20896 * @cfg {String} format
20897 * The default date format string which can be overriden for localization support. The format must be
20898 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20902 * @cfg {String} altFormats
20903 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20904 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20906 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20914 todayHighlight : false,
20920 keyboardNavigation: true,
20922 calendarWeeks: false,
20924 startDate: -Infinity,
20928 daysOfWeekDisabled: [],
20932 singleMode : false,
20934 UTCDate: function()
20936 return new Date(Date.UTC.apply(Date, arguments));
20939 UTCToday: function()
20941 var today = new Date();
20942 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20945 getDate: function() {
20946 var d = this.getUTCDate();
20947 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20950 getUTCDate: function() {
20954 setDate: function(d) {
20955 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20958 setUTCDate: function(d) {
20960 this.setValue(this.formatDate(this.date));
20963 onRender: function(ct, position)
20966 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20968 this.language = this.language || 'en';
20969 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20970 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20972 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20973 this.format = this.format || 'm/d/y';
20974 this.isInline = false;
20975 this.isInput = true;
20976 this.component = this.el.select('.add-on', true).first() || false;
20977 this.component = (this.component && this.component.length === 0) ? false : this.component;
20978 this.hasInput = this.component && this.inputEl().length;
20980 if (typeof(this.minViewMode === 'string')) {
20981 switch (this.minViewMode) {
20983 this.minViewMode = 1;
20986 this.minViewMode = 2;
20989 this.minViewMode = 0;
20994 if (typeof(this.viewMode === 'string')) {
20995 switch (this.viewMode) {
21008 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21010 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21012 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21014 this.picker().on('mousedown', this.onMousedown, this);
21015 this.picker().on('click', this.onClick, this);
21017 this.picker().addClass('datepicker-dropdown');
21019 this.startViewMode = this.viewMode;
21021 if(this.singleMode){
21022 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21023 v.setVisibilityMode(Roo.Element.DISPLAY);
21027 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21028 v.setStyle('width', '189px');
21032 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21033 if(!this.calendarWeeks){
21038 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21039 v.attr('colspan', function(i, val){
21040 return parseInt(val) + 1;
21045 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21047 this.setStartDate(this.startDate);
21048 this.setEndDate(this.endDate);
21050 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21057 if(this.isInline) {
21062 picker : function()
21064 return this.pickerEl;
21065 // return this.el.select('.datepicker', true).first();
21068 fillDow: function()
21070 var dowCnt = this.weekStart;
21079 if(this.calendarWeeks){
21087 while (dowCnt < this.weekStart + 7) {
21091 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21095 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21098 fillMonths: function()
21101 var months = this.picker().select('>.datepicker-months td', true).first();
21103 months.dom.innerHTML = '';
21109 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21112 months.createChild(month);
21119 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;
21121 if (this.date < this.startDate) {
21122 this.viewDate = new Date(this.startDate);
21123 } else if (this.date > this.endDate) {
21124 this.viewDate = new Date(this.endDate);
21126 this.viewDate = new Date(this.date);
21134 var d = new Date(this.viewDate),
21135 year = d.getUTCFullYear(),
21136 month = d.getUTCMonth(),
21137 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21138 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21139 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21140 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21141 currentDate = this.date && this.date.valueOf(),
21142 today = this.UTCToday();
21144 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21146 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21148 // this.picker.select('>tfoot th.today').
21149 // .text(dates[this.language].today)
21150 // .toggle(this.todayBtn !== false);
21152 this.updateNavArrows();
21155 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21157 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21159 prevMonth.setUTCDate(day);
21161 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21163 var nextMonth = new Date(prevMonth);
21165 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21167 nextMonth = nextMonth.valueOf();
21169 var fillMonths = false;
21171 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21173 while(prevMonth.valueOf() <= nextMonth) {
21176 if (prevMonth.getUTCDay() === this.weekStart) {
21178 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21186 if(this.calendarWeeks){
21187 // ISO 8601: First week contains first thursday.
21188 // ISO also states week starts on Monday, but we can be more abstract here.
21190 // Start of current week: based on weekstart/current date
21191 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21192 // Thursday of this week
21193 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21194 // First Thursday of year, year from thursday
21195 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21196 // Calendar week: ms between thursdays, div ms per day, div 7 days
21197 calWeek = (th - yth) / 864e5 / 7 + 1;
21199 fillMonths.cn.push({
21207 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21209 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21212 if (this.todayHighlight &&
21213 prevMonth.getUTCFullYear() == today.getFullYear() &&
21214 prevMonth.getUTCMonth() == today.getMonth() &&
21215 prevMonth.getUTCDate() == today.getDate()) {
21216 clsName += ' today';
21219 if (currentDate && prevMonth.valueOf() === currentDate) {
21220 clsName += ' active';
21223 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21224 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21225 clsName += ' disabled';
21228 fillMonths.cn.push({
21230 cls: 'day ' + clsName,
21231 html: prevMonth.getDate()
21234 prevMonth.setDate(prevMonth.getDate()+1);
21237 var currentYear = this.date && this.date.getUTCFullYear();
21238 var currentMonth = this.date && this.date.getUTCMonth();
21240 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21242 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21243 v.removeClass('active');
21245 if(currentYear === year && k === currentMonth){
21246 v.addClass('active');
21249 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21250 v.addClass('disabled');
21256 year = parseInt(year/10, 10) * 10;
21258 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21260 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21263 for (var i = -1; i < 11; i++) {
21264 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21266 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21274 showMode: function(dir)
21277 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21280 Roo.each(this.picker().select('>div',true).elements, function(v){
21281 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21284 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21289 if(this.isInline) {
21293 this.picker().removeClass(['bottom', 'top']);
21295 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21297 * place to the top of element!
21301 this.picker().addClass('top');
21302 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21307 this.picker().addClass('bottom');
21309 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21312 parseDate : function(value)
21314 if(!value || value instanceof Date){
21317 var v = Date.parseDate(value, this.format);
21318 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21319 v = Date.parseDate(value, 'Y-m-d');
21321 if(!v && this.altFormats){
21322 if(!this.altFormatsArray){
21323 this.altFormatsArray = this.altFormats.split("|");
21325 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21326 v = Date.parseDate(value, this.altFormatsArray[i]);
21332 formatDate : function(date, fmt)
21334 return (!date || !(date instanceof Date)) ?
21335 date : date.dateFormat(fmt || this.format);
21338 onFocus : function()
21340 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21344 onBlur : function()
21346 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21348 var d = this.inputEl().getValue();
21355 showPopup : function()
21357 this.picker().show();
21361 this.fireEvent('showpopup', this, this.date);
21364 hidePopup : function()
21366 if(this.isInline) {
21369 this.picker().hide();
21370 this.viewMode = this.startViewMode;
21373 this.fireEvent('hidepopup', this, this.date);
21377 onMousedown: function(e)
21379 e.stopPropagation();
21380 e.preventDefault();
21385 Roo.bootstrap.DateField.superclass.keyup.call(this);
21389 setValue: function(v)
21391 if(this.fireEvent('beforeselect', this, v) !== false){
21392 var d = new Date(this.parseDate(v) ).clearTime();
21394 if(isNaN(d.getTime())){
21395 this.date = this.viewDate = '';
21396 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21400 v = this.formatDate(d);
21402 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21404 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21408 this.fireEvent('select', this, this.date);
21412 getValue: function()
21414 return this.formatDate(this.date);
21417 fireKey: function(e)
21419 if (!this.picker().isVisible()){
21420 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21426 var dateChanged = false,
21428 newDate, newViewDate;
21433 e.preventDefault();
21437 if (!this.keyboardNavigation) {
21440 dir = e.keyCode == 37 ? -1 : 1;
21443 newDate = this.moveYear(this.date, dir);
21444 newViewDate = this.moveYear(this.viewDate, dir);
21445 } else if (e.shiftKey){
21446 newDate = this.moveMonth(this.date, dir);
21447 newViewDate = this.moveMonth(this.viewDate, dir);
21449 newDate = new Date(this.date);
21450 newDate.setUTCDate(this.date.getUTCDate() + dir);
21451 newViewDate = new Date(this.viewDate);
21452 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21454 if (this.dateWithinRange(newDate)){
21455 this.date = newDate;
21456 this.viewDate = newViewDate;
21457 this.setValue(this.formatDate(this.date));
21459 e.preventDefault();
21460 dateChanged = true;
21465 if (!this.keyboardNavigation) {
21468 dir = e.keyCode == 38 ? -1 : 1;
21470 newDate = this.moveYear(this.date, dir);
21471 newViewDate = this.moveYear(this.viewDate, dir);
21472 } else if (e.shiftKey){
21473 newDate = this.moveMonth(this.date, dir);
21474 newViewDate = this.moveMonth(this.viewDate, dir);
21476 newDate = new Date(this.date);
21477 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21478 newViewDate = new Date(this.viewDate);
21479 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21481 if (this.dateWithinRange(newDate)){
21482 this.date = newDate;
21483 this.viewDate = newViewDate;
21484 this.setValue(this.formatDate(this.date));
21486 e.preventDefault();
21487 dateChanged = true;
21491 this.setValue(this.formatDate(this.date));
21493 e.preventDefault();
21496 this.setValue(this.formatDate(this.date));
21510 onClick: function(e)
21512 e.stopPropagation();
21513 e.preventDefault();
21515 var target = e.getTarget();
21517 if(target.nodeName.toLowerCase() === 'i'){
21518 target = Roo.get(target).dom.parentNode;
21521 var nodeName = target.nodeName;
21522 var className = target.className;
21523 var html = target.innerHTML;
21524 //Roo.log(nodeName);
21526 switch(nodeName.toLowerCase()) {
21528 switch(className) {
21534 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21535 switch(this.viewMode){
21537 this.viewDate = this.moveMonth(this.viewDate, dir);
21541 this.viewDate = this.moveYear(this.viewDate, dir);
21547 var date = new Date();
21548 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21550 this.setValue(this.formatDate(this.date));
21557 if (className.indexOf('disabled') < 0) {
21558 this.viewDate.setUTCDate(1);
21559 if (className.indexOf('month') > -1) {
21560 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21562 var year = parseInt(html, 10) || 0;
21563 this.viewDate.setUTCFullYear(year);
21567 if(this.singleMode){
21568 this.setValue(this.formatDate(this.viewDate));
21579 //Roo.log(className);
21580 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21581 var day = parseInt(html, 10) || 1;
21582 var year = this.viewDate.getUTCFullYear(),
21583 month = this.viewDate.getUTCMonth();
21585 if (className.indexOf('old') > -1) {
21592 } else if (className.indexOf('new') > -1) {
21600 //Roo.log([year,month,day]);
21601 this.date = this.UTCDate(year, month, day,0,0,0,0);
21602 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21604 //Roo.log(this.formatDate(this.date));
21605 this.setValue(this.formatDate(this.date));
21612 setStartDate: function(startDate)
21614 this.startDate = startDate || -Infinity;
21615 if (this.startDate !== -Infinity) {
21616 this.startDate = this.parseDate(this.startDate);
21619 this.updateNavArrows();
21622 setEndDate: function(endDate)
21624 this.endDate = endDate || Infinity;
21625 if (this.endDate !== Infinity) {
21626 this.endDate = this.parseDate(this.endDate);
21629 this.updateNavArrows();
21632 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21634 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21635 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21636 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21638 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21639 return parseInt(d, 10);
21642 this.updateNavArrows();
21645 updateNavArrows: function()
21647 if(this.singleMode){
21651 var d = new Date(this.viewDate),
21652 year = d.getUTCFullYear(),
21653 month = d.getUTCMonth();
21655 Roo.each(this.picker().select('.prev', true).elements, function(v){
21657 switch (this.viewMode) {
21660 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21666 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21673 Roo.each(this.picker().select('.next', true).elements, function(v){
21675 switch (this.viewMode) {
21678 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21684 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21692 moveMonth: function(date, dir)
21697 var new_date = new Date(date.valueOf()),
21698 day = new_date.getUTCDate(),
21699 month = new_date.getUTCMonth(),
21700 mag = Math.abs(dir),
21702 dir = dir > 0 ? 1 : -1;
21705 // If going back one month, make sure month is not current month
21706 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21708 return new_date.getUTCMonth() == month;
21710 // If going forward one month, make sure month is as expected
21711 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21713 return new_date.getUTCMonth() != new_month;
21715 new_month = month + dir;
21716 new_date.setUTCMonth(new_month);
21717 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21718 if (new_month < 0 || new_month > 11) {
21719 new_month = (new_month + 12) % 12;
21722 // For magnitudes >1, move one month at a time...
21723 for (var i=0; i<mag; i++) {
21724 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21725 new_date = this.moveMonth(new_date, dir);
21727 // ...then reset the day, keeping it in the new month
21728 new_month = new_date.getUTCMonth();
21729 new_date.setUTCDate(day);
21731 return new_month != new_date.getUTCMonth();
21734 // Common date-resetting loop -- if date is beyond end of month, make it
21737 new_date.setUTCDate(--day);
21738 new_date.setUTCMonth(new_month);
21743 moveYear: function(date, dir)
21745 return this.moveMonth(date, dir*12);
21748 dateWithinRange: function(date)
21750 return date >= this.startDate && date <= this.endDate;
21756 this.picker().remove();
21759 validateValue : function(value)
21761 if(this.getVisibilityEl().hasClass('hidden')){
21765 if(value.length < 1) {
21766 if(this.allowBlank){
21772 if(value.length < this.minLength){
21775 if(value.length > this.maxLength){
21779 var vt = Roo.form.VTypes;
21780 if(!vt[this.vtype](value, this)){
21784 if(typeof this.validator == "function"){
21785 var msg = this.validator(value);
21791 if(this.regex && !this.regex.test(value)){
21795 if(typeof(this.parseDate(value)) == 'undefined'){
21799 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21803 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21813 this.date = this.viewDate = '';
21815 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21820 Roo.apply(Roo.bootstrap.DateField, {
21831 html: '<i class="fa fa-arrow-left"/>'
21841 html: '<i class="fa fa-arrow-right"/>'
21883 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21884 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21885 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21886 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21887 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21900 navFnc: 'FullYear',
21905 navFnc: 'FullYear',
21910 Roo.apply(Roo.bootstrap.DateField, {
21914 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21918 cls: 'datepicker-days',
21922 cls: 'table-condensed',
21924 Roo.bootstrap.DateField.head,
21928 Roo.bootstrap.DateField.footer
21935 cls: 'datepicker-months',
21939 cls: 'table-condensed',
21941 Roo.bootstrap.DateField.head,
21942 Roo.bootstrap.DateField.content,
21943 Roo.bootstrap.DateField.footer
21950 cls: 'datepicker-years',
21954 cls: 'table-condensed',
21956 Roo.bootstrap.DateField.head,
21957 Roo.bootstrap.DateField.content,
21958 Roo.bootstrap.DateField.footer
21977 * @class Roo.bootstrap.TimeField
21978 * @extends Roo.bootstrap.Input
21979 * Bootstrap DateField class
21983 * Create a new TimeField
21984 * @param {Object} config The config object
21987 Roo.bootstrap.TimeField = function(config){
21988 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21992 * Fires when this field show.
21993 * @param {Roo.bootstrap.DateField} thisthis
21994 * @param {Mixed} date The date value
21999 * Fires when this field hide.
22000 * @param {Roo.bootstrap.DateField} this
22001 * @param {Mixed} date The date value
22006 * Fires when select a date.
22007 * @param {Roo.bootstrap.DateField} this
22008 * @param {Mixed} date The date value
22014 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22017 * @cfg {String} format
22018 * The default time format string which can be overriden for localization support. The format must be
22019 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22023 getAutoCreate : function()
22025 this.after = '<i class="fa far fa-clock"></i>';
22026 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22030 onRender: function(ct, position)
22033 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22035 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22037 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22039 this.pop = this.picker().select('>.datepicker-time',true).first();
22040 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22042 this.picker().on('mousedown', this.onMousedown, this);
22043 this.picker().on('click', this.onClick, this);
22045 this.picker().addClass('datepicker-dropdown');
22050 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22051 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22052 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22053 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22054 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22055 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22059 fireKey: function(e){
22060 if (!this.picker().isVisible()){
22061 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22067 e.preventDefault();
22075 this.onTogglePeriod();
22078 this.onIncrementMinutes();
22081 this.onDecrementMinutes();
22090 onClick: function(e) {
22091 e.stopPropagation();
22092 e.preventDefault();
22095 picker : function()
22097 return this.pickerEl;
22100 fillTime: function()
22102 var time = this.pop.select('tbody', true).first();
22104 time.dom.innerHTML = '';
22119 cls: 'hours-up fa fas fa-chevron-up'
22139 cls: 'minutes-up fa fas fa-chevron-up'
22160 cls: 'timepicker-hour',
22175 cls: 'timepicker-minute',
22190 cls: 'btn btn-primary period',
22212 cls: 'hours-down fa fas fa-chevron-down'
22232 cls: 'minutes-down fa fas fa-chevron-down'
22250 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22257 var hours = this.time.getHours();
22258 var minutes = this.time.getMinutes();
22271 hours = hours - 12;
22275 hours = '0' + hours;
22279 minutes = '0' + minutes;
22282 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22283 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22284 this.pop.select('button', true).first().dom.innerHTML = period;
22290 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22292 var cls = ['bottom'];
22294 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22301 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22305 //this.picker().setXY(20000,20000);
22306 this.picker().addClass(cls.join('-'));
22310 Roo.each(cls, function(c){
22315 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22316 //_this.picker().setTop(_this.inputEl().getHeight());
22320 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22322 //_this.picker().setTop(0 - _this.picker().getHeight());
22327 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22331 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22339 onFocus : function()
22341 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22345 onBlur : function()
22347 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22353 this.picker().show();
22358 this.fireEvent('show', this, this.date);
22363 this.picker().hide();
22366 this.fireEvent('hide', this, this.date);
22369 setTime : function()
22372 this.setValue(this.time.format(this.format));
22374 this.fireEvent('select', this, this.date);
22379 onMousedown: function(e){
22380 e.stopPropagation();
22381 e.preventDefault();
22384 onIncrementHours: function()
22386 Roo.log('onIncrementHours');
22387 this.time = this.time.add(Date.HOUR, 1);
22392 onDecrementHours: function()
22394 Roo.log('onDecrementHours');
22395 this.time = this.time.add(Date.HOUR, -1);
22399 onIncrementMinutes: function()
22401 Roo.log('onIncrementMinutes');
22402 this.time = this.time.add(Date.MINUTE, 1);
22406 onDecrementMinutes: function()
22408 Roo.log('onDecrementMinutes');
22409 this.time = this.time.add(Date.MINUTE, -1);
22413 onTogglePeriod: function()
22415 Roo.log('onTogglePeriod');
22416 this.time = this.time.add(Date.HOUR, 12);
22424 Roo.apply(Roo.bootstrap.TimeField, {
22428 cls: 'datepicker dropdown-menu',
22432 cls: 'datepicker-time',
22436 cls: 'table-condensed',
22465 cls: 'btn btn-info ok',
22493 * @class Roo.bootstrap.MonthField
22494 * @extends Roo.bootstrap.Input
22495 * Bootstrap MonthField class
22497 * @cfg {String} language default en
22500 * Create a new MonthField
22501 * @param {Object} config The config object
22504 Roo.bootstrap.MonthField = function(config){
22505 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22510 * Fires when this field show.
22511 * @param {Roo.bootstrap.MonthField} this
22512 * @param {Mixed} date The date value
22517 * Fires when this field hide.
22518 * @param {Roo.bootstrap.MonthField} this
22519 * @param {Mixed} date The date value
22524 * Fires when select a date.
22525 * @param {Roo.bootstrap.MonthField} this
22526 * @param {String} oldvalue The old value
22527 * @param {String} newvalue The new value
22533 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22535 onRender: function(ct, position)
22538 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22540 this.language = this.language || 'en';
22541 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22542 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22544 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22545 this.isInline = false;
22546 this.isInput = true;
22547 this.component = this.el.select('.add-on', true).first() || false;
22548 this.component = (this.component && this.component.length === 0) ? false : this.component;
22549 this.hasInput = this.component && this.inputEL().length;
22551 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22553 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22555 this.picker().on('mousedown', this.onMousedown, this);
22556 this.picker().on('click', this.onClick, this);
22558 this.picker().addClass('datepicker-dropdown');
22560 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22561 v.setStyle('width', '189px');
22568 if(this.isInline) {
22574 setValue: function(v, suppressEvent)
22576 var o = this.getValue();
22578 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22582 if(suppressEvent !== true){
22583 this.fireEvent('select', this, o, v);
22588 getValue: function()
22593 onClick: function(e)
22595 e.stopPropagation();
22596 e.preventDefault();
22598 var target = e.getTarget();
22600 if(target.nodeName.toLowerCase() === 'i'){
22601 target = Roo.get(target).dom.parentNode;
22604 var nodeName = target.nodeName;
22605 var className = target.className;
22606 var html = target.innerHTML;
22608 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22612 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22614 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22620 picker : function()
22622 return this.pickerEl;
22625 fillMonths: function()
22628 var months = this.picker().select('>.datepicker-months td', true).first();
22630 months.dom.innerHTML = '';
22636 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22639 months.createChild(month);
22648 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22649 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22652 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22653 e.removeClass('active');
22655 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22656 e.addClass('active');
22663 if(this.isInline) {
22667 this.picker().removeClass(['bottom', 'top']);
22669 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22671 * place to the top of element!
22675 this.picker().addClass('top');
22676 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22681 this.picker().addClass('bottom');
22683 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22686 onFocus : function()
22688 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22692 onBlur : function()
22694 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22696 var d = this.inputEl().getValue();
22705 this.picker().show();
22706 this.picker().select('>.datepicker-months', true).first().show();
22710 this.fireEvent('show', this, this.date);
22715 if(this.isInline) {
22718 this.picker().hide();
22719 this.fireEvent('hide', this, this.date);
22723 onMousedown: function(e)
22725 e.stopPropagation();
22726 e.preventDefault();
22731 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22735 fireKey: function(e)
22737 if (!this.picker().isVisible()){
22738 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22749 e.preventDefault();
22753 dir = e.keyCode == 37 ? -1 : 1;
22755 this.vIndex = this.vIndex + dir;
22757 if(this.vIndex < 0){
22761 if(this.vIndex > 11){
22765 if(isNaN(this.vIndex)){
22769 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22775 dir = e.keyCode == 38 ? -1 : 1;
22777 this.vIndex = this.vIndex + dir * 4;
22779 if(this.vIndex < 0){
22783 if(this.vIndex > 11){
22787 if(isNaN(this.vIndex)){
22791 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22796 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22797 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22801 e.preventDefault();
22804 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22805 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22821 this.picker().remove();
22826 Roo.apply(Roo.bootstrap.MonthField, {
22845 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22846 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22851 Roo.apply(Roo.bootstrap.MonthField, {
22855 cls: 'datepicker dropdown-menu roo-dynamic',
22859 cls: 'datepicker-months',
22863 cls: 'table-condensed',
22865 Roo.bootstrap.DateField.content
22885 * @class Roo.bootstrap.CheckBox
22886 * @extends Roo.bootstrap.Input
22887 * Bootstrap CheckBox class
22889 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22890 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22891 * @cfg {String} boxLabel The text that appears beside the checkbox
22892 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22893 * @cfg {Boolean} checked initnal the element
22894 * @cfg {Boolean} inline inline the element (default false)
22895 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22896 * @cfg {String} tooltip label tooltip
22899 * Create a new CheckBox
22900 * @param {Object} config The config object
22903 Roo.bootstrap.CheckBox = function(config){
22904 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22909 * Fires when the element is checked or unchecked.
22910 * @param {Roo.bootstrap.CheckBox} this This input
22911 * @param {Boolean} checked The new checked value
22916 * Fires when the element is click.
22917 * @param {Roo.bootstrap.CheckBox} this This input
22924 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22926 inputType: 'checkbox',
22935 // checkbox success does not make any sense really..
22940 getAutoCreate : function()
22942 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22948 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22951 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22957 type : this.inputType,
22958 value : this.inputValue,
22959 cls : 'roo-' + this.inputType, //'form-box',
22960 placeholder : this.placeholder || ''
22964 if(this.inputType != 'radio'){
22968 cls : 'roo-hidden-value',
22969 value : this.checked ? this.inputValue : this.valueOff
22974 if (this.weight) { // Validity check?
22975 cfg.cls += " " + this.inputType + "-" + this.weight;
22978 if (this.disabled) {
22979 input.disabled=true;
22983 input.checked = this.checked;
22988 input.name = this.name;
22990 if(this.inputType != 'radio'){
22991 hidden.name = this.name;
22992 input.name = '_hidden_' + this.name;
22997 input.cls += ' input-' + this.size;
23002 ['xs','sm','md','lg'].map(function(size){
23003 if (settings[size]) {
23004 cfg.cls += ' col-' + size + '-' + settings[size];
23008 var inputblock = input;
23010 if (this.before || this.after) {
23013 cls : 'input-group',
23018 inputblock.cn.push({
23020 cls : 'input-group-addon',
23025 inputblock.cn.push(input);
23027 if(this.inputType != 'radio'){
23028 inputblock.cn.push(hidden);
23032 inputblock.cn.push({
23034 cls : 'input-group-addon',
23040 var boxLabelCfg = false;
23046 //'for': id, // box label is handled by onclick - so no for...
23048 html: this.boxLabel
23051 boxLabelCfg.tooltip = this.tooltip;
23057 if (align ==='left' && this.fieldLabel.length) {
23058 // Roo.log("left and has label");
23063 cls : 'control-label',
23064 html : this.fieldLabel
23075 cfg.cn[1].cn.push(boxLabelCfg);
23078 if(this.labelWidth > 12){
23079 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23082 if(this.labelWidth < 13 && this.labelmd == 0){
23083 this.labelmd = this.labelWidth;
23086 if(this.labellg > 0){
23087 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23088 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23091 if(this.labelmd > 0){
23092 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23093 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23096 if(this.labelsm > 0){
23097 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23098 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23101 if(this.labelxs > 0){
23102 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23103 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23106 } else if ( this.fieldLabel.length) {
23107 // Roo.log(" label");
23111 tag: this.boxLabel ? 'span' : 'label',
23113 cls: 'control-label box-input-label',
23114 //cls : 'input-group-addon',
23115 html : this.fieldLabel
23122 cfg.cn.push(boxLabelCfg);
23127 // Roo.log(" no label && no align");
23128 cfg.cn = [ inputblock ] ;
23130 cfg.cn.push(boxLabelCfg);
23138 if(this.inputType != 'radio'){
23139 cfg.cn.push(hidden);
23147 * return the real input element.
23149 inputEl: function ()
23151 return this.el.select('input.roo-' + this.inputType,true).first();
23153 hiddenEl: function ()
23155 return this.el.select('input.roo-hidden-value',true).first();
23158 labelEl: function()
23160 return this.el.select('label.control-label',true).first();
23162 /* depricated... */
23166 return this.labelEl();
23169 boxLabelEl: function()
23171 return this.el.select('label.box-label',true).first();
23174 initEvents : function()
23176 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23178 this.inputEl().on('click', this.onClick, this);
23180 if (this.boxLabel) {
23181 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23184 this.startValue = this.getValue();
23187 Roo.bootstrap.CheckBox.register(this);
23191 onClick : function(e)
23193 if(this.fireEvent('click', this, e) !== false){
23194 this.setChecked(!this.checked);
23199 setChecked : function(state,suppressEvent)
23201 this.startValue = this.getValue();
23203 if(this.inputType == 'radio'){
23205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23206 e.dom.checked = false;
23209 this.inputEl().dom.checked = true;
23211 this.inputEl().dom.value = this.inputValue;
23213 if(suppressEvent !== true){
23214 this.fireEvent('check', this, true);
23222 this.checked = state;
23224 this.inputEl().dom.checked = state;
23227 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23229 if(suppressEvent !== true){
23230 this.fireEvent('check', this, state);
23236 getValue : function()
23238 if(this.inputType == 'radio'){
23239 return this.getGroupValue();
23242 return this.hiddenEl().dom.value;
23246 getGroupValue : function()
23248 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23252 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23255 setValue : function(v,suppressEvent)
23257 if(this.inputType == 'radio'){
23258 this.setGroupValue(v, suppressEvent);
23262 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23267 setGroupValue : function(v, suppressEvent)
23269 this.startValue = this.getValue();
23271 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23272 e.dom.checked = false;
23274 if(e.dom.value == v){
23275 e.dom.checked = true;
23279 if(suppressEvent !== true){
23280 this.fireEvent('check', this, true);
23288 validate : function()
23290 if(this.getVisibilityEl().hasClass('hidden')){
23296 (this.inputType == 'radio' && this.validateRadio()) ||
23297 (this.inputType == 'checkbox' && this.validateCheckbox())
23303 this.markInvalid();
23307 validateRadio : function()
23309 if(this.getVisibilityEl().hasClass('hidden')){
23313 if(this.allowBlank){
23319 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23320 if(!e.dom.checked){
23332 validateCheckbox : function()
23335 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23336 //return (this.getValue() == this.inputValue) ? true : false;
23339 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23347 for(var i in group){
23348 if(group[i].el.isVisible(true)){
23356 for(var i in group){
23361 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23368 * Mark this field as valid
23370 markValid : function()
23374 this.fireEvent('valid', this);
23376 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23379 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23386 if(this.inputType == 'radio'){
23387 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23388 var fg = e.findParent('.form-group', false, true);
23389 if (Roo.bootstrap.version == 3) {
23390 fg.removeClass([_this.invalidClass, _this.validClass]);
23391 fg.addClass(_this.validClass);
23393 fg.removeClass(['is-valid', 'is-invalid']);
23394 fg.addClass('is-valid');
23402 var fg = this.el.findParent('.form-group', false, true);
23403 if (Roo.bootstrap.version == 3) {
23404 fg.removeClass([this.invalidClass, this.validClass]);
23405 fg.addClass(this.validClass);
23407 fg.removeClass(['is-valid', 'is-invalid']);
23408 fg.addClass('is-valid');
23413 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23419 for(var i in group){
23420 var fg = group[i].el.findParent('.form-group', false, true);
23421 if (Roo.bootstrap.version == 3) {
23422 fg.removeClass([this.invalidClass, this.validClass]);
23423 fg.addClass(this.validClass);
23425 fg.removeClass(['is-valid', 'is-invalid']);
23426 fg.addClass('is-valid');
23432 * Mark this field as invalid
23433 * @param {String} msg The validation message
23435 markInvalid : function(msg)
23437 if(this.allowBlank){
23443 this.fireEvent('invalid', this, msg);
23445 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23448 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23452 label.markInvalid();
23455 if(this.inputType == 'radio'){
23457 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23458 var fg = e.findParent('.form-group', false, true);
23459 if (Roo.bootstrap.version == 3) {
23460 fg.removeClass([_this.invalidClass, _this.validClass]);
23461 fg.addClass(_this.invalidClass);
23463 fg.removeClass(['is-invalid', 'is-valid']);
23464 fg.addClass('is-invalid');
23472 var fg = this.el.findParent('.form-group', false, true);
23473 if (Roo.bootstrap.version == 3) {
23474 fg.removeClass([_this.invalidClass, _this.validClass]);
23475 fg.addClass(_this.invalidClass);
23477 fg.removeClass(['is-invalid', 'is-valid']);
23478 fg.addClass('is-invalid');
23483 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23489 for(var i in group){
23490 var fg = group[i].el.findParent('.form-group', false, true);
23491 if (Roo.bootstrap.version == 3) {
23492 fg.removeClass([_this.invalidClass, _this.validClass]);
23493 fg.addClass(_this.invalidClass);
23495 fg.removeClass(['is-invalid', 'is-valid']);
23496 fg.addClass('is-invalid');
23502 clearInvalid : function()
23504 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23506 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23508 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23510 if (label && label.iconEl) {
23511 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23512 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23516 disable : function()
23518 if(this.inputType != 'radio'){
23519 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23526 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23527 _this.getActionEl().addClass(this.disabledClass);
23528 e.dom.disabled = true;
23532 this.disabled = true;
23533 this.fireEvent("disable", this);
23537 enable : function()
23539 if(this.inputType != 'radio'){
23540 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23547 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23548 _this.getActionEl().removeClass(this.disabledClass);
23549 e.dom.disabled = false;
23553 this.disabled = false;
23554 this.fireEvent("enable", this);
23558 setBoxLabel : function(v)
23563 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23569 Roo.apply(Roo.bootstrap.CheckBox, {
23574 * register a CheckBox Group
23575 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23577 register : function(checkbox)
23579 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23580 this.groups[checkbox.groupId] = {};
23583 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23587 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23591 * fetch a CheckBox Group based on the group ID
23592 * @param {string} the group ID
23593 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23595 get: function(groupId) {
23596 if (typeof(this.groups[groupId]) == 'undefined') {
23600 return this.groups[groupId] ;
23613 * @class Roo.bootstrap.Radio
23614 * @extends Roo.bootstrap.Component
23615 * Bootstrap Radio class
23616 * @cfg {String} boxLabel - the label associated
23617 * @cfg {String} value - the value of radio
23620 * Create a new Radio
23621 * @param {Object} config The config object
23623 Roo.bootstrap.Radio = function(config){
23624 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23628 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23634 getAutoCreate : function()
23638 cls : 'form-group radio',
23643 html : this.boxLabel
23651 initEvents : function()
23653 this.parent().register(this);
23655 this.el.on('click', this.onClick, this);
23659 onClick : function(e)
23661 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23662 this.setChecked(true);
23666 setChecked : function(state, suppressEvent)
23668 this.parent().setValue(this.value, suppressEvent);
23672 setBoxLabel : function(v)
23677 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23692 * @class Roo.bootstrap.SecurePass
23693 * @extends Roo.bootstrap.Input
23694 * Bootstrap SecurePass class
23698 * Create a new SecurePass
23699 * @param {Object} config The config object
23702 Roo.bootstrap.SecurePass = function (config) {
23703 // these go here, so the translation tool can replace them..
23705 PwdEmpty: "Please type a password, and then retype it to confirm.",
23706 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23707 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23708 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23709 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23710 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23711 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23712 TooWeak: "Your password is Too Weak."
23714 this.meterLabel = "Password strength:";
23715 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23716 this.meterClass = [
23717 "roo-password-meter-tooweak",
23718 "roo-password-meter-weak",
23719 "roo-password-meter-medium",
23720 "roo-password-meter-strong",
23721 "roo-password-meter-grey"
23726 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23729 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23731 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23733 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23734 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23735 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23736 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23737 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23738 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23739 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23749 * @cfg {String/Object} Label for the strength meter (defaults to
23750 * 'Password strength:')
23755 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23756 * ['Weak', 'Medium', 'Strong'])
23759 pwdStrengths: false,
23772 initEvents: function ()
23774 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23776 if (this.el.is('input[type=password]') && Roo.isSafari) {
23777 this.el.on('keydown', this.SafariOnKeyDown, this);
23780 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23783 onRender: function (ct, position)
23785 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23786 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23787 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23789 this.trigger.createChild({
23794 cls: 'roo-password-meter-grey col-xs-12',
23797 //width: this.meterWidth + 'px'
23801 cls: 'roo-password-meter-text'
23807 if (this.hideTrigger) {
23808 this.trigger.setDisplayed(false);
23810 this.setSize(this.width || '', this.height || '');
23813 onDestroy: function ()
23815 if (this.trigger) {
23816 this.trigger.removeAllListeners();
23817 this.trigger.remove();
23820 this.wrap.remove();
23822 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23825 checkStrength: function ()
23827 var pwd = this.inputEl().getValue();
23828 if (pwd == this._lastPwd) {
23833 if (this.ClientSideStrongPassword(pwd)) {
23835 } else if (this.ClientSideMediumPassword(pwd)) {
23837 } else if (this.ClientSideWeakPassword(pwd)) {
23843 Roo.log('strength1: ' + strength);
23845 //var pm = this.trigger.child('div/div/div').dom;
23846 var pm = this.trigger.child('div/div');
23847 pm.removeClass(this.meterClass);
23848 pm.addClass(this.meterClass[strength]);
23851 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23853 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23855 this._lastPwd = pwd;
23859 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23861 this._lastPwd = '';
23863 var pm = this.trigger.child('div/div');
23864 pm.removeClass(this.meterClass);
23865 pm.addClass('roo-password-meter-grey');
23868 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23871 this.inputEl().dom.type='password';
23874 validateValue: function (value)
23876 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23879 if (value.length == 0) {
23880 if (this.allowBlank) {
23881 this.clearInvalid();
23885 this.markInvalid(this.errors.PwdEmpty);
23886 this.errorMsg = this.errors.PwdEmpty;
23894 if (!value.match(/[\x21-\x7e]+/)) {
23895 this.markInvalid(this.errors.PwdBadChar);
23896 this.errorMsg = this.errors.PwdBadChar;
23899 if (value.length < 6) {
23900 this.markInvalid(this.errors.PwdShort);
23901 this.errorMsg = this.errors.PwdShort;
23904 if (value.length > 16) {
23905 this.markInvalid(this.errors.PwdLong);
23906 this.errorMsg = this.errors.PwdLong;
23910 if (this.ClientSideStrongPassword(value)) {
23912 } else if (this.ClientSideMediumPassword(value)) {
23914 } else if (this.ClientSideWeakPassword(value)) {
23921 if (strength < 2) {
23922 //this.markInvalid(this.errors.TooWeak);
23923 this.errorMsg = this.errors.TooWeak;
23928 console.log('strength2: ' + strength);
23930 //var pm = this.trigger.child('div/div/div').dom;
23932 var pm = this.trigger.child('div/div');
23933 pm.removeClass(this.meterClass);
23934 pm.addClass(this.meterClass[strength]);
23936 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23938 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23940 this.errorMsg = '';
23944 CharacterSetChecks: function (type)
23947 this.fResult = false;
23950 isctype: function (character, type)
23953 case this.kCapitalLetter:
23954 if (character >= 'A' && character <= 'Z') {
23959 case this.kSmallLetter:
23960 if (character >= 'a' && character <= 'z') {
23966 if (character >= '0' && character <= '9') {
23971 case this.kPunctuation:
23972 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23983 IsLongEnough: function (pwd, size)
23985 return !(pwd == null || isNaN(size) || pwd.length < size);
23988 SpansEnoughCharacterSets: function (word, nb)
23990 if (!this.IsLongEnough(word, nb))
23995 var characterSetChecks = new Array(
23996 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23997 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24000 for (var index = 0; index < word.length; ++index) {
24001 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24002 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24003 characterSetChecks[nCharSet].fResult = true;
24010 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24011 if (characterSetChecks[nCharSet].fResult) {
24016 if (nCharSets < nb) {
24022 ClientSideStrongPassword: function (pwd)
24024 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24027 ClientSideMediumPassword: function (pwd)
24029 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24032 ClientSideWeakPassword: function (pwd)
24034 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24037 })//<script type="text/javascript">
24040 * Based Ext JS Library 1.1.1
24041 * Copyright(c) 2006-2007, Ext JS, LLC.
24047 * @class Roo.HtmlEditorCore
24048 * @extends Roo.Component
24049 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24051 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24054 Roo.HtmlEditorCore = function(config){
24057 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24062 * @event initialize
24063 * Fires when the editor is fully initialized (including the iframe)
24064 * @param {Roo.HtmlEditorCore} this
24069 * Fires when the editor is first receives the focus. Any insertion must wait
24070 * until after this event.
24071 * @param {Roo.HtmlEditorCore} this
24075 * @event beforesync
24076 * Fires before the textarea is updated with content from the editor iframe. Return false
24077 * to cancel the sync.
24078 * @param {Roo.HtmlEditorCore} this
24079 * @param {String} html
24083 * @event beforepush
24084 * Fires before the iframe editor is updated with content from the textarea. Return false
24085 * to cancel the push.
24086 * @param {Roo.HtmlEditorCore} this
24087 * @param {String} html
24092 * Fires when the textarea is updated with content from the editor iframe.
24093 * @param {Roo.HtmlEditorCore} this
24094 * @param {String} html
24099 * Fires when the iframe editor is updated with content from the textarea.
24100 * @param {Roo.HtmlEditorCore} this
24101 * @param {String} html
24106 * @event editorevent
24107 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24108 * @param {Roo.HtmlEditorCore} this
24114 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24116 // defaults : white / black...
24117 this.applyBlacklists();
24124 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24128 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24134 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24139 * @cfg {Number} height (in pixels)
24143 * @cfg {Number} width (in pixels)
24148 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24151 stylesheets: false,
24156 // private properties
24157 validationEvent : false,
24159 initialized : false,
24161 sourceEditMode : false,
24162 onFocus : Roo.emptyFn,
24164 hideMode:'offsets',
24168 // blacklist + whitelisted elements..
24175 * Protected method that will not generally be called directly. It
24176 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24177 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24179 getDocMarkup : function(){
24183 // inherit styels from page...??
24184 if (this.stylesheets === false) {
24186 Roo.get(document.head).select('style').each(function(node) {
24187 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24190 Roo.get(document.head).select('link').each(function(node) {
24191 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24194 } else if (!this.stylesheets.length) {
24196 st = '<style type="text/css">' +
24197 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24200 for (var i in this.stylesheets) {
24201 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24206 st += '<style type="text/css">' +
24207 'IMG { cursor: pointer } ' +
24210 var cls = 'roo-htmleditor-body';
24212 if(this.bodyCls.length){
24213 cls += ' ' + this.bodyCls;
24216 return '<html><head>' + st +
24217 //<style type="text/css">' +
24218 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24220 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24224 onRender : function(ct, position)
24227 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24228 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24231 this.el.dom.style.border = '0 none';
24232 this.el.dom.setAttribute('tabIndex', -1);
24233 this.el.addClass('x-hidden hide');
24237 if(Roo.isIE){ // fix IE 1px bogus margin
24238 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24242 this.frameId = Roo.id();
24246 var iframe = this.owner.wrap.createChild({
24248 cls: 'form-control', // bootstrap..
24250 name: this.frameId,
24251 frameBorder : 'no',
24252 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24257 this.iframe = iframe.dom;
24259 this.assignDocWin();
24261 this.doc.designMode = 'on';
24264 this.doc.write(this.getDocMarkup());
24268 var task = { // must defer to wait for browser to be ready
24270 //console.log("run task?" + this.doc.readyState);
24271 this.assignDocWin();
24272 if(this.doc.body || this.doc.readyState == 'complete'){
24274 this.doc.designMode="on";
24278 Roo.TaskMgr.stop(task);
24279 this.initEditor.defer(10, this);
24286 Roo.TaskMgr.start(task);
24291 onResize : function(w, h)
24293 Roo.log('resize: ' +w + ',' + h );
24294 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24298 if(typeof w == 'number'){
24300 this.iframe.style.width = w + 'px';
24302 if(typeof h == 'number'){
24304 this.iframe.style.height = h + 'px';
24306 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24313 * Toggles the editor between standard and source edit mode.
24314 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24316 toggleSourceEdit : function(sourceEditMode){
24318 this.sourceEditMode = sourceEditMode === true;
24320 if(this.sourceEditMode){
24322 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24325 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24326 //this.iframe.className = '';
24329 //this.setSize(this.owner.wrap.getSize());
24330 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24337 * Protected method that will not generally be called directly. If you need/want
24338 * custom HTML cleanup, this is the method you should override.
24339 * @param {String} html The HTML to be cleaned
24340 * return {String} The cleaned HTML
24342 cleanHtml : function(html){
24343 html = String(html);
24344 if(html.length > 5){
24345 if(Roo.isSafari){ // strip safari nonsense
24346 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24349 if(html == ' '){
24356 * HTML Editor -> Textarea
24357 * Protected method that will not generally be called directly. Syncs the contents
24358 * of the editor iframe with the textarea.
24360 syncValue : function(){
24361 if(this.initialized){
24362 var bd = (this.doc.body || this.doc.documentElement);
24363 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24364 var html = bd.innerHTML;
24366 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24367 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24369 html = '<div style="'+m[0]+'">' + html + '</div>';
24372 html = this.cleanHtml(html);
24373 // fix up the special chars.. normaly like back quotes in word...
24374 // however we do not want to do this with chinese..
24375 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24377 var cc = match.charCodeAt();
24379 // Get the character value, handling surrogate pairs
24380 if (match.length == 2) {
24381 // It's a surrogate pair, calculate the Unicode code point
24382 var high = match.charCodeAt(0) - 0xD800;
24383 var low = match.charCodeAt(1) - 0xDC00;
24384 cc = (high * 0x400) + low + 0x10000;
24386 (cc >= 0x4E00 && cc < 0xA000 ) ||
24387 (cc >= 0x3400 && cc < 0x4E00 ) ||
24388 (cc >= 0xf900 && cc < 0xfb00 )
24393 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24394 return "&#" + cc + ";";
24401 if(this.owner.fireEvent('beforesync', this, html) !== false){
24402 this.el.dom.value = html;
24403 this.owner.fireEvent('sync', this, html);
24409 * Protected method that will not generally be called directly. Pushes the value of the textarea
24410 * into the iframe editor.
24412 pushValue : function(){
24413 if(this.initialized){
24414 var v = this.el.dom.value.trim();
24416 // if(v.length < 1){
24420 if(this.owner.fireEvent('beforepush', this, v) !== false){
24421 var d = (this.doc.body || this.doc.documentElement);
24423 this.cleanUpPaste();
24424 this.el.dom.value = d.innerHTML;
24425 this.owner.fireEvent('push', this, v);
24431 deferFocus : function(){
24432 this.focus.defer(10, this);
24436 focus : function(){
24437 if(this.win && !this.sourceEditMode){
24444 assignDocWin: function()
24446 var iframe = this.iframe;
24449 this.doc = iframe.contentWindow.document;
24450 this.win = iframe.contentWindow;
24452 // if (!Roo.get(this.frameId)) {
24455 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24456 // this.win = Roo.get(this.frameId).dom.contentWindow;
24458 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24462 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24463 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24468 initEditor : function(){
24469 //console.log("INIT EDITOR");
24470 this.assignDocWin();
24474 this.doc.designMode="on";
24476 this.doc.write(this.getDocMarkup());
24479 var dbody = (this.doc.body || this.doc.documentElement);
24480 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24481 // this copies styles from the containing element into thsi one..
24482 // not sure why we need all of this..
24483 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24485 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24486 //ss['background-attachment'] = 'fixed'; // w3c
24487 dbody.bgProperties = 'fixed'; // ie
24488 //Roo.DomHelper.applyStyles(dbody, ss);
24489 Roo.EventManager.on(this.doc, {
24490 //'mousedown': this.onEditorEvent,
24491 'mouseup': this.onEditorEvent,
24492 'dblclick': this.onEditorEvent,
24493 'click': this.onEditorEvent,
24494 'keyup': this.onEditorEvent,
24499 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24501 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24502 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24504 this.initialized = true;
24506 this.owner.fireEvent('initialize', this);
24511 onDestroy : function(){
24517 //for (var i =0; i < this.toolbars.length;i++) {
24518 // // fixme - ask toolbars for heights?
24519 // this.toolbars[i].onDestroy();
24522 //this.wrap.dom.innerHTML = '';
24523 //this.wrap.remove();
24528 onFirstFocus : function(){
24530 this.assignDocWin();
24533 this.activated = true;
24536 if(Roo.isGecko){ // prevent silly gecko errors
24538 var s = this.win.getSelection();
24539 if(!s.focusNode || s.focusNode.nodeType != 3){
24540 var r = s.getRangeAt(0);
24541 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24546 this.execCmd('useCSS', true);
24547 this.execCmd('styleWithCSS', false);
24550 this.owner.fireEvent('activate', this);
24554 adjustFont: function(btn){
24555 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24556 //if(Roo.isSafari){ // safari
24559 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24560 if(Roo.isSafari){ // safari
24561 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24562 v = (v < 10) ? 10 : v;
24563 v = (v > 48) ? 48 : v;
24564 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24569 v = Math.max(1, v+adjust);
24571 this.execCmd('FontSize', v );
24574 onEditorEvent : function(e)
24576 this.owner.fireEvent('editorevent', this, e);
24577 // this.updateToolbar();
24578 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24581 insertTag : function(tg)
24583 // could be a bit smarter... -> wrap the current selected tRoo..
24584 if (tg.toLowerCase() == 'span' ||
24585 tg.toLowerCase() == 'code' ||
24586 tg.toLowerCase() == 'sup' ||
24587 tg.toLowerCase() == 'sub'
24590 range = this.createRange(this.getSelection());
24591 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24592 wrappingNode.appendChild(range.extractContents());
24593 range.insertNode(wrappingNode);
24600 this.execCmd("formatblock", tg);
24604 insertText : function(txt)
24608 var range = this.createRange();
24609 range.deleteContents();
24610 //alert(Sender.getAttribute('label'));
24612 range.insertNode(this.doc.createTextNode(txt));
24618 * Executes a Midas editor command on the editor document and performs necessary focus and
24619 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24620 * @param {String} cmd The Midas command
24621 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24623 relayCmd : function(cmd, value){
24625 this.execCmd(cmd, value);
24626 this.owner.fireEvent('editorevent', this);
24627 //this.updateToolbar();
24628 this.owner.deferFocus();
24632 * Executes a Midas editor command directly on the editor document.
24633 * For visual commands, you should use {@link #relayCmd} instead.
24634 * <b>This should only be called after the editor is initialized.</b>
24635 * @param {String} cmd The Midas command
24636 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24638 execCmd : function(cmd, value){
24639 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24646 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24648 * @param {String} text | dom node..
24650 insertAtCursor : function(text)
24653 if(!this.activated){
24659 var r = this.doc.selection.createRange();
24670 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24674 // from jquery ui (MIT licenced)
24676 var win = this.win;
24678 if (win.getSelection && win.getSelection().getRangeAt) {
24679 range = win.getSelection().getRangeAt(0);
24680 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24681 range.insertNode(node);
24682 } else if (win.document.selection && win.document.selection.createRange) {
24683 // no firefox support
24684 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24685 win.document.selection.createRange().pasteHTML(txt);
24687 // no firefox support
24688 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24689 this.execCmd('InsertHTML', txt);
24698 mozKeyPress : function(e){
24700 var c = e.getCharCode(), cmd;
24703 c = String.fromCharCode(c).toLowerCase();
24717 this.cleanUpPaste.defer(100, this);
24725 e.preventDefault();
24733 fixKeys : function(){ // load time branching for fastest keydown performance
24735 return function(e){
24736 var k = e.getKey(), r;
24739 r = this.doc.selection.createRange();
24742 r.pasteHTML('    ');
24749 r = this.doc.selection.createRange();
24751 var target = r.parentElement();
24752 if(!target || target.tagName.toLowerCase() != 'li'){
24754 r.pasteHTML('<br />');
24760 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24761 this.cleanUpPaste.defer(100, this);
24767 }else if(Roo.isOpera){
24768 return function(e){
24769 var k = e.getKey();
24773 this.execCmd('InsertHTML','    ');
24776 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24777 this.cleanUpPaste.defer(100, this);
24782 }else if(Roo.isSafari){
24783 return function(e){
24784 var k = e.getKey();
24788 this.execCmd('InsertText','\t');
24792 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24793 this.cleanUpPaste.defer(100, this);
24801 getAllAncestors: function()
24803 var p = this.getSelectedNode();
24806 a.push(p); // push blank onto stack..
24807 p = this.getParentElement();
24811 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24815 a.push(this.doc.body);
24819 lastSelNode : false,
24822 getSelection : function()
24824 this.assignDocWin();
24825 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24828 getSelectedNode: function()
24830 // this may only work on Gecko!!!
24832 // should we cache this!!!!
24837 var range = this.createRange(this.getSelection()).cloneRange();
24840 var parent = range.parentElement();
24842 var testRange = range.duplicate();
24843 testRange.moveToElementText(parent);
24844 if (testRange.inRange(range)) {
24847 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24850 parent = parent.parentElement;
24855 // is ancestor a text element.
24856 var ac = range.commonAncestorContainer;
24857 if (ac.nodeType == 3) {
24858 ac = ac.parentNode;
24861 var ar = ac.childNodes;
24864 var other_nodes = [];
24865 var has_other_nodes = false;
24866 for (var i=0;i<ar.length;i++) {
24867 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24870 // fullly contained node.
24872 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24877 // probably selected..
24878 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24879 other_nodes.push(ar[i]);
24883 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24888 has_other_nodes = true;
24890 if (!nodes.length && other_nodes.length) {
24891 nodes= other_nodes;
24893 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24899 createRange: function(sel)
24901 // this has strange effects when using with
24902 // top toolbar - not sure if it's a great idea.
24903 //this.editor.contentWindow.focus();
24904 if (typeof sel != "undefined") {
24906 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24908 return this.doc.createRange();
24911 return this.doc.createRange();
24914 getParentElement: function()
24917 this.assignDocWin();
24918 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24920 var range = this.createRange(sel);
24923 var p = range.commonAncestorContainer;
24924 while (p.nodeType == 3) { // text node
24935 * Range intersection.. the hard stuff...
24939 * [ -- selected range --- ]
24943 * if end is before start or hits it. fail.
24944 * if start is after end or hits it fail.
24946 * if either hits (but other is outside. - then it's not
24952 // @see http://www.thismuchiknow.co.uk/?p=64.
24953 rangeIntersectsNode : function(range, node)
24955 var nodeRange = node.ownerDocument.createRange();
24957 nodeRange.selectNode(node);
24959 nodeRange.selectNodeContents(node);
24962 var rangeStartRange = range.cloneRange();
24963 rangeStartRange.collapse(true);
24965 var rangeEndRange = range.cloneRange();
24966 rangeEndRange.collapse(false);
24968 var nodeStartRange = nodeRange.cloneRange();
24969 nodeStartRange.collapse(true);
24971 var nodeEndRange = nodeRange.cloneRange();
24972 nodeEndRange.collapse(false);
24974 return rangeStartRange.compareBoundaryPoints(
24975 Range.START_TO_START, nodeEndRange) == -1 &&
24976 rangeEndRange.compareBoundaryPoints(
24977 Range.START_TO_START, nodeStartRange) == 1;
24981 rangeCompareNode : function(range, node)
24983 var nodeRange = node.ownerDocument.createRange();
24985 nodeRange.selectNode(node);
24987 nodeRange.selectNodeContents(node);
24991 range.collapse(true);
24993 nodeRange.collapse(true);
24995 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24996 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24998 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25000 var nodeIsBefore = ss == 1;
25001 var nodeIsAfter = ee == -1;
25003 if (nodeIsBefore && nodeIsAfter) {
25006 if (!nodeIsBefore && nodeIsAfter) {
25007 return 1; //right trailed.
25010 if (nodeIsBefore && !nodeIsAfter) {
25011 return 2; // left trailed.
25017 // private? - in a new class?
25018 cleanUpPaste : function()
25020 // cleans up the whole document..
25021 Roo.log('cleanuppaste');
25023 this.cleanUpChildren(this.doc.body);
25024 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25025 if (clean != this.doc.body.innerHTML) {
25026 this.doc.body.innerHTML = clean;
25031 cleanWordChars : function(input) {// change the chars to hex code
25032 var he = Roo.HtmlEditorCore;
25034 var output = input;
25035 Roo.each(he.swapCodes, function(sw) {
25036 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25038 output = output.replace(swapper, sw[1]);
25045 cleanUpChildren : function (n)
25047 if (!n.childNodes.length) {
25050 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25051 this.cleanUpChild(n.childNodes[i]);
25058 cleanUpChild : function (node)
25061 //console.log(node);
25062 if (node.nodeName == "#text") {
25063 // clean up silly Windows -- stuff?
25066 if (node.nodeName == "#comment") {
25067 node.parentNode.removeChild(node);
25068 // clean up silly Windows -- stuff?
25071 var lcname = node.tagName.toLowerCase();
25072 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25073 // whitelist of tags..
25075 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25077 node.parentNode.removeChild(node);
25082 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25084 // spans with no attributes - just remove them..
25085 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25086 remove_keep_children = true;
25089 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25090 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25092 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25093 // remove_keep_children = true;
25096 if (remove_keep_children) {
25097 this.cleanUpChildren(node);
25098 // inserts everything just before this node...
25099 while (node.childNodes.length) {
25100 var cn = node.childNodes[0];
25101 node.removeChild(cn);
25102 node.parentNode.insertBefore(cn, node);
25104 node.parentNode.removeChild(node);
25108 if (!node.attributes || !node.attributes.length) {
25113 this.cleanUpChildren(node);
25117 function cleanAttr(n,v)
25120 if (v.match(/^\./) || v.match(/^\//)) {
25123 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25126 if (v.match(/^#/)) {
25129 if (v.match(/^\{/)) { // allow template editing.
25132 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25133 node.removeAttribute(n);
25137 var cwhite = this.cwhite;
25138 var cblack = this.cblack;
25140 function cleanStyle(n,v)
25142 if (v.match(/expression/)) { //XSS?? should we even bother..
25143 node.removeAttribute(n);
25147 var parts = v.split(/;/);
25150 Roo.each(parts, function(p) {
25151 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25155 var l = p.split(':').shift().replace(/\s+/g,'');
25156 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25158 if ( cwhite.length && cblack.indexOf(l) > -1) {
25159 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25160 //node.removeAttribute(n);
25164 // only allow 'c whitelisted system attributes'
25165 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25166 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25167 //node.removeAttribute(n);
25177 if (clean.length) {
25178 node.setAttribute(n, clean.join(';'));
25180 node.removeAttribute(n);
25186 for (var i = node.attributes.length-1; i > -1 ; i--) {
25187 var a = node.attributes[i];
25190 if (a.name.toLowerCase().substr(0,2)=='on') {
25191 node.removeAttribute(a.name);
25194 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25195 node.removeAttribute(a.name);
25198 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25199 cleanAttr(a.name,a.value); // fixme..
25202 if (a.name == 'style') {
25203 cleanStyle(a.name,a.value);
25206 /// clean up MS crap..
25207 // tecnically this should be a list of valid class'es..
25210 if (a.name == 'class') {
25211 if (a.value.match(/^Mso/)) {
25212 node.removeAttribute('class');
25215 if (a.value.match(/^body$/)) {
25216 node.removeAttribute('class');
25227 this.cleanUpChildren(node);
25233 * Clean up MS wordisms...
25235 cleanWord : function(node)
25238 this.cleanWord(this.doc.body);
25243 node.nodeName == 'SPAN' &&
25244 !node.hasAttributes() &&
25245 node.childNodes.length == 1 &&
25246 node.firstChild.nodeName == "#text"
25248 var textNode = node.firstChild;
25249 node.removeChild(textNode);
25250 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25251 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25253 node.parentNode.insertBefore(textNode, node);
25254 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25255 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25257 node.parentNode.removeChild(node);
25260 if (node.nodeName == "#text") {
25261 // clean up silly Windows -- stuff?
25264 if (node.nodeName == "#comment") {
25265 node.parentNode.removeChild(node);
25266 // clean up silly Windows -- stuff?
25270 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25271 node.parentNode.removeChild(node);
25274 //Roo.log(node.tagName);
25275 // remove - but keep children..
25276 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25277 //Roo.log('-- removed');
25278 while (node.childNodes.length) {
25279 var cn = node.childNodes[0];
25280 node.removeChild(cn);
25281 node.parentNode.insertBefore(cn, node);
25282 // move node to parent - and clean it..
25283 this.cleanWord(cn);
25285 node.parentNode.removeChild(node);
25286 /// no need to iterate chidlren = it's got none..
25287 //this.iterateChildren(node, this.cleanWord);
25291 if (node.className.length) {
25293 var cn = node.className.split(/\W+/);
25295 Roo.each(cn, function(cls) {
25296 if (cls.match(/Mso[a-zA-Z]+/)) {
25301 node.className = cna.length ? cna.join(' ') : '';
25303 node.removeAttribute("class");
25307 if (node.hasAttribute("lang")) {
25308 node.removeAttribute("lang");
25311 if (node.hasAttribute("style")) {
25313 var styles = node.getAttribute("style").split(";");
25315 Roo.each(styles, function(s) {
25316 if (!s.match(/:/)) {
25319 var kv = s.split(":");
25320 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25323 // what ever is left... we allow.
25326 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25327 if (!nstyle.length) {
25328 node.removeAttribute('style');
25331 this.iterateChildren(node, this.cleanWord);
25337 * iterateChildren of a Node, calling fn each time, using this as the scole..
25338 * @param {DomNode} node node to iterate children of.
25339 * @param {Function} fn method of this class to call on each item.
25341 iterateChildren : function(node, fn)
25343 if (!node.childNodes.length) {
25346 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25347 fn.call(this, node.childNodes[i])
25353 * cleanTableWidths.
25355 * Quite often pasting from word etc.. results in tables with column and widths.
25356 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25359 cleanTableWidths : function(node)
25364 this.cleanTableWidths(this.doc.body);
25369 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25372 Roo.log(node.tagName);
25373 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25374 this.iterateChildren(node, this.cleanTableWidths);
25377 if (node.hasAttribute('width')) {
25378 node.removeAttribute('width');
25382 if (node.hasAttribute("style")) {
25385 var styles = node.getAttribute("style").split(";");
25387 Roo.each(styles, function(s) {
25388 if (!s.match(/:/)) {
25391 var kv = s.split(":");
25392 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25395 // what ever is left... we allow.
25398 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25399 if (!nstyle.length) {
25400 node.removeAttribute('style');
25404 this.iterateChildren(node, this.cleanTableWidths);
25412 domToHTML : function(currentElement, depth, nopadtext) {
25414 depth = depth || 0;
25415 nopadtext = nopadtext || false;
25417 if (!currentElement) {
25418 return this.domToHTML(this.doc.body);
25421 //Roo.log(currentElement);
25423 var allText = false;
25424 var nodeName = currentElement.nodeName;
25425 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25427 if (nodeName == '#text') {
25429 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25434 if (nodeName != 'BODY') {
25437 // Prints the node tagName, such as <A>, <IMG>, etc
25440 for(i = 0; i < currentElement.attributes.length;i++) {
25442 var aname = currentElement.attributes.item(i).name;
25443 if (!currentElement.attributes.item(i).value.length) {
25446 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25449 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25458 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25461 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25466 // Traverse the tree
25468 var currentElementChild = currentElement.childNodes.item(i);
25469 var allText = true;
25470 var innerHTML = '';
25472 while (currentElementChild) {
25473 // Formatting code (indent the tree so it looks nice on the screen)
25474 var nopad = nopadtext;
25475 if (lastnode == 'SPAN') {
25479 if (currentElementChild.nodeName == '#text') {
25480 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25481 toadd = nopadtext ? toadd : toadd.trim();
25482 if (!nopad && toadd.length > 80) {
25483 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25485 innerHTML += toadd;
25488 currentElementChild = currentElement.childNodes.item(i);
25494 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25496 // Recursively traverse the tree structure of the child node
25497 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25498 lastnode = currentElementChild.nodeName;
25500 currentElementChild=currentElement.childNodes.item(i);
25506 // The remaining code is mostly for formatting the tree
25507 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25512 ret+= "</"+tagName+">";
25518 applyBlacklists : function()
25520 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25521 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25525 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25526 if (b.indexOf(tag) > -1) {
25529 this.white.push(tag);
25533 Roo.each(w, function(tag) {
25534 if (b.indexOf(tag) > -1) {
25537 if (this.white.indexOf(tag) > -1) {
25540 this.white.push(tag);
25545 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25546 if (w.indexOf(tag) > -1) {
25549 this.black.push(tag);
25553 Roo.each(b, function(tag) {
25554 if (w.indexOf(tag) > -1) {
25557 if (this.black.indexOf(tag) > -1) {
25560 this.black.push(tag);
25565 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25566 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25570 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25571 if (b.indexOf(tag) > -1) {
25574 this.cwhite.push(tag);
25578 Roo.each(w, function(tag) {
25579 if (b.indexOf(tag) > -1) {
25582 if (this.cwhite.indexOf(tag) > -1) {
25585 this.cwhite.push(tag);
25590 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25591 if (w.indexOf(tag) > -1) {
25594 this.cblack.push(tag);
25598 Roo.each(b, function(tag) {
25599 if (w.indexOf(tag) > -1) {
25602 if (this.cblack.indexOf(tag) > -1) {
25605 this.cblack.push(tag);
25610 setStylesheets : function(stylesheets)
25612 if(typeof(stylesheets) == 'string'){
25613 Roo.get(this.iframe.contentDocument.head).createChild({
25615 rel : 'stylesheet',
25624 Roo.each(stylesheets, function(s) {
25629 Roo.get(_this.iframe.contentDocument.head).createChild({
25631 rel : 'stylesheet',
25640 removeStylesheets : function()
25644 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25649 setStyle : function(style)
25651 Roo.get(this.iframe.contentDocument.head).createChild({
25660 // hide stuff that is not compatible
25674 * @event specialkey
25678 * @cfg {String} fieldClass @hide
25681 * @cfg {String} focusClass @hide
25684 * @cfg {String} autoCreate @hide
25687 * @cfg {String} inputType @hide
25690 * @cfg {String} invalidClass @hide
25693 * @cfg {String} invalidText @hide
25696 * @cfg {String} msgFx @hide
25699 * @cfg {String} validateOnBlur @hide
25703 Roo.HtmlEditorCore.white = [
25704 'area', 'br', 'img', 'input', 'hr', 'wbr',
25706 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25707 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25708 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25709 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25710 'table', 'ul', 'xmp',
25712 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25715 'dir', 'menu', 'ol', 'ul', 'dl',
25721 Roo.HtmlEditorCore.black = [
25722 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25724 'base', 'basefont', 'bgsound', 'blink', 'body',
25725 'frame', 'frameset', 'head', 'html', 'ilayer',
25726 'iframe', 'layer', 'link', 'meta', 'object',
25727 'script', 'style' ,'title', 'xml' // clean later..
25729 Roo.HtmlEditorCore.clean = [
25730 'script', 'style', 'title', 'xml'
25732 Roo.HtmlEditorCore.remove = [
25737 Roo.HtmlEditorCore.ablack = [
25741 Roo.HtmlEditorCore.aclean = [
25742 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25746 Roo.HtmlEditorCore.pwhite= [
25747 'http', 'https', 'mailto'
25750 // white listed style attributes.
25751 Roo.HtmlEditorCore.cwhite= [
25752 // 'text-align', /// default is to allow most things..
25758 // black listed style attributes.
25759 Roo.HtmlEditorCore.cblack= [
25760 // 'font-size' -- this can be set by the project
25764 Roo.HtmlEditorCore.swapCodes =[
25783 * @class Roo.bootstrap.HtmlEditor
25784 * @extends Roo.bootstrap.TextArea
25785 * Bootstrap HtmlEditor class
25788 * Create a new HtmlEditor
25789 * @param {Object} config The config object
25792 Roo.bootstrap.HtmlEditor = function(config){
25793 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25794 if (!this.toolbars) {
25795 this.toolbars = [];
25798 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25801 * @event initialize
25802 * Fires when the editor is fully initialized (including the iframe)
25803 * @param {HtmlEditor} this
25808 * Fires when the editor is first receives the focus. Any insertion must wait
25809 * until after this event.
25810 * @param {HtmlEditor} this
25814 * @event beforesync
25815 * Fires before the textarea is updated with content from the editor iframe. Return false
25816 * to cancel the sync.
25817 * @param {HtmlEditor} this
25818 * @param {String} html
25822 * @event beforepush
25823 * Fires before the iframe editor is updated with content from the textarea. Return false
25824 * to cancel the push.
25825 * @param {HtmlEditor} this
25826 * @param {String} html
25831 * Fires when the textarea is updated with content from the editor iframe.
25832 * @param {HtmlEditor} this
25833 * @param {String} html
25838 * Fires when the iframe editor is updated with content from the textarea.
25839 * @param {HtmlEditor} this
25840 * @param {String} html
25844 * @event editmodechange
25845 * Fires when the editor switches edit modes
25846 * @param {HtmlEditor} this
25847 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25849 editmodechange: true,
25851 * @event editorevent
25852 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25853 * @param {HtmlEditor} this
25857 * @event firstfocus
25858 * Fires when on first focus - needed by toolbars..
25859 * @param {HtmlEditor} this
25864 * Auto save the htmlEditor value as a file into Events
25865 * @param {HtmlEditor} this
25869 * @event savedpreview
25870 * preview the saved version of htmlEditor
25871 * @param {HtmlEditor} this
25878 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25882 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25887 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25892 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25897 * @cfg {Number} height (in pixels)
25901 * @cfg {Number} width (in pixels)
25906 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25909 stylesheets: false,
25914 // private properties
25915 validationEvent : false,
25917 initialized : false,
25920 onFocus : Roo.emptyFn,
25922 hideMode:'offsets',
25924 tbContainer : false,
25928 toolbarContainer :function() {
25929 return this.wrap.select('.x-html-editor-tb',true).first();
25933 * Protected method that will not generally be called directly. It
25934 * is called when the editor creates its toolbar. Override this method if you need to
25935 * add custom toolbar buttons.
25936 * @param {HtmlEditor} editor
25938 createToolbar : function(){
25939 Roo.log('renewing');
25940 Roo.log("create toolbars");
25942 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25943 this.toolbars[0].render(this.toolbarContainer());
25947 // if (!editor.toolbars || !editor.toolbars.length) {
25948 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25951 // for (var i =0 ; i < editor.toolbars.length;i++) {
25952 // editor.toolbars[i] = Roo.factory(
25953 // typeof(editor.toolbars[i]) == 'string' ?
25954 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25955 // Roo.bootstrap.HtmlEditor);
25956 // editor.toolbars[i].init(editor);
25962 onRender : function(ct, position)
25964 // Roo.log("Call onRender: " + this.xtype);
25966 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25968 this.wrap = this.inputEl().wrap({
25969 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25972 this.editorcore.onRender(ct, position);
25974 if (this.resizable) {
25975 this.resizeEl = new Roo.Resizable(this.wrap, {
25979 minHeight : this.height,
25980 height: this.height,
25981 handles : this.resizable,
25984 resize : function(r, w, h) {
25985 _t.onResize(w,h); // -something
25991 this.createToolbar(this);
25994 if(!this.width && this.resizable){
25995 this.setSize(this.wrap.getSize());
25997 if (this.resizeEl) {
25998 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25999 // should trigger onReize..
26005 onResize : function(w, h)
26007 Roo.log('resize: ' +w + ',' + h );
26008 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26012 if(this.inputEl() ){
26013 if(typeof w == 'number'){
26014 var aw = w - this.wrap.getFrameWidth('lr');
26015 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26018 if(typeof h == 'number'){
26019 var tbh = -11; // fixme it needs to tool bar size!
26020 for (var i =0; i < this.toolbars.length;i++) {
26021 // fixme - ask toolbars for heights?
26022 tbh += this.toolbars[i].el.getHeight();
26023 //if (this.toolbars[i].footer) {
26024 // tbh += this.toolbars[i].footer.el.getHeight();
26032 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26033 ah -= 5; // knock a few pixes off for look..
26034 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26038 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26039 this.editorcore.onResize(ew,eh);
26044 * Toggles the editor between standard and source edit mode.
26045 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26047 toggleSourceEdit : function(sourceEditMode)
26049 this.editorcore.toggleSourceEdit(sourceEditMode);
26051 if(this.editorcore.sourceEditMode){
26052 Roo.log('editor - showing textarea');
26055 // Roo.log(this.syncValue());
26057 this.inputEl().removeClass(['hide', 'x-hidden']);
26058 this.inputEl().dom.removeAttribute('tabIndex');
26059 this.inputEl().focus();
26061 Roo.log('editor - hiding textarea');
26063 // Roo.log(this.pushValue());
26066 this.inputEl().addClass(['hide', 'x-hidden']);
26067 this.inputEl().dom.setAttribute('tabIndex', -1);
26068 //this.deferFocus();
26071 if(this.resizable){
26072 this.setSize(this.wrap.getSize());
26075 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26078 // private (for BoxComponent)
26079 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26081 // private (for BoxComponent)
26082 getResizeEl : function(){
26086 // private (for BoxComponent)
26087 getPositionEl : function(){
26092 initEvents : function(){
26093 this.originalValue = this.getValue();
26097 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26100 // markInvalid : Roo.emptyFn,
26102 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26105 // clearInvalid : Roo.emptyFn,
26107 setValue : function(v){
26108 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26109 this.editorcore.pushValue();
26114 deferFocus : function(){
26115 this.focus.defer(10, this);
26119 focus : function(){
26120 this.editorcore.focus();
26126 onDestroy : function(){
26132 for (var i =0; i < this.toolbars.length;i++) {
26133 // fixme - ask toolbars for heights?
26134 this.toolbars[i].onDestroy();
26137 this.wrap.dom.innerHTML = '';
26138 this.wrap.remove();
26143 onFirstFocus : function(){
26144 //Roo.log("onFirstFocus");
26145 this.editorcore.onFirstFocus();
26146 for (var i =0; i < this.toolbars.length;i++) {
26147 this.toolbars[i].onFirstFocus();
26153 syncValue : function()
26155 this.editorcore.syncValue();
26158 pushValue : function()
26160 this.editorcore.pushValue();
26164 // hide stuff that is not compatible
26178 * @event specialkey
26182 * @cfg {String} fieldClass @hide
26185 * @cfg {String} focusClass @hide
26188 * @cfg {String} autoCreate @hide
26191 * @cfg {String} inputType @hide
26195 * @cfg {String} invalidText @hide
26198 * @cfg {String} msgFx @hide
26201 * @cfg {String} validateOnBlur @hide
26210 Roo.namespace('Roo.bootstrap.htmleditor');
26212 * @class Roo.bootstrap.HtmlEditorToolbar1
26218 new Roo.bootstrap.HtmlEditor({
26221 new Roo.bootstrap.HtmlEditorToolbar1({
26222 disable : { fonts: 1 , format: 1, ..., ... , ...],
26228 * @cfg {Object} disable List of elements to disable..
26229 * @cfg {Array} btns List of additional buttons.
26233 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26236 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26239 Roo.apply(this, config);
26241 // default disabled, based on 'good practice'..
26242 this.disable = this.disable || {};
26243 Roo.applyIf(this.disable, {
26246 specialElements : true
26248 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26250 this.editor = config.editor;
26251 this.editorcore = config.editor.editorcore;
26253 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26255 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26256 // dont call parent... till later.
26258 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26263 editorcore : false,
26268 "h1","h2","h3","h4","h5","h6",
26270 "abbr", "acronym", "address", "cite", "samp", "var",
26274 onRender : function(ct, position)
26276 // Roo.log("Call onRender: " + this.xtype);
26278 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26280 this.el.dom.style.marginBottom = '0';
26282 var editorcore = this.editorcore;
26283 var editor= this.editor;
26286 var btn = function(id,cmd , toggle, handler, html){
26288 var event = toggle ? 'toggle' : 'click';
26293 xns: Roo.bootstrap,
26297 enableToggle:toggle !== false,
26299 pressed : toggle ? false : null,
26302 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26303 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26309 // var cb_box = function...
26314 xns: Roo.bootstrap,
26319 xns: Roo.bootstrap,
26323 Roo.each(this.formats, function(f) {
26324 style.menu.items.push({
26326 xns: Roo.bootstrap,
26327 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26332 editorcore.insertTag(this.tagname);
26339 children.push(style);
26341 btn('bold',false,true);
26342 btn('italic',false,true);
26343 btn('align-left', 'justifyleft',true);
26344 btn('align-center', 'justifycenter',true);
26345 btn('align-right' , 'justifyright',true);
26346 btn('link', false, false, function(btn) {
26347 //Roo.log("create link?");
26348 var url = prompt(this.createLinkText, this.defaultLinkValue);
26349 if(url && url != 'http:/'+'/'){
26350 this.editorcore.relayCmd('createlink', url);
26353 btn('list','insertunorderedlist',true);
26354 btn('pencil', false,true, function(btn){
26356 this.toggleSourceEdit(btn.pressed);
26359 if (this.editor.btns.length > 0) {
26360 for (var i = 0; i<this.editor.btns.length; i++) {
26361 children.push(this.editor.btns[i]);
26369 xns: Roo.bootstrap,
26374 xns: Roo.bootstrap,
26379 cog.menu.items.push({
26381 xns: Roo.bootstrap,
26382 html : Clean styles,
26387 editorcore.insertTag(this.tagname);
26396 this.xtype = 'NavSimplebar';
26398 for(var i=0;i< children.length;i++) {
26400 this.buttons.add(this.addxtypeChild(children[i]));
26404 editor.on('editorevent', this.updateToolbar, this);
26406 onBtnClick : function(id)
26408 this.editorcore.relayCmd(id);
26409 this.editorcore.focus();
26413 * Protected method that will not generally be called directly. It triggers
26414 * a toolbar update by reading the markup state of the current selection in the editor.
26416 updateToolbar: function(){
26418 if(!this.editorcore.activated){
26419 this.editor.onFirstFocus(); // is this neeed?
26423 var btns = this.buttons;
26424 var doc = this.editorcore.doc;
26425 btns.get('bold').setActive(doc.queryCommandState('bold'));
26426 btns.get('italic').setActive(doc.queryCommandState('italic'));
26427 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26429 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26430 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26431 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26433 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26434 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26437 var ans = this.editorcore.getAllAncestors();
26438 if (this.formatCombo) {
26441 var store = this.formatCombo.store;
26442 this.formatCombo.setValue("");
26443 for (var i =0; i < ans.length;i++) {
26444 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26446 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26454 // hides menus... - so this cant be on a menu...
26455 Roo.bootstrap.MenuMgr.hideAll();
26457 Roo.bootstrap.MenuMgr.hideAll();
26458 //this.editorsyncValue();
26460 onFirstFocus: function() {
26461 this.buttons.each(function(item){
26465 toggleSourceEdit : function(sourceEditMode){
26468 if(sourceEditMode){
26469 Roo.log("disabling buttons");
26470 this.buttons.each( function(item){
26471 if(item.cmd != 'pencil'){
26477 Roo.log("enabling buttons");
26478 if(this.editorcore.initialized){
26479 this.buttons.each( function(item){
26485 Roo.log("calling toggole on editor");
26486 // tell the editor that it's been pressed..
26487 this.editor.toggleSourceEdit(sourceEditMode);
26501 * @class Roo.bootstrap.Markdown
26502 * @extends Roo.bootstrap.TextArea
26503 * Bootstrap Showdown editable area
26504 * @cfg {string} content
26507 * Create a new Showdown
26510 Roo.bootstrap.Markdown = function(config){
26511 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26515 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26519 initEvents : function()
26522 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26523 this.markdownEl = this.el.createChild({
26524 cls : 'roo-markdown-area'
26526 this.inputEl().addClass('d-none');
26527 if (this.getValue() == '') {
26528 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26531 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26533 this.markdownEl.on('click', this.toggleTextEdit, this);
26534 this.on('blur', this.toggleTextEdit, this);
26535 this.on('specialkey', this.resizeTextArea, this);
26538 toggleTextEdit : function()
26540 var sh = this.markdownEl.getHeight();
26541 this.inputEl().addClass('d-none');
26542 this.markdownEl.addClass('d-none');
26543 if (!this.editing) {
26545 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26546 this.inputEl().removeClass('d-none');
26547 this.inputEl().focus();
26548 this.editing = true;
26551 // show showdown...
26552 this.updateMarkdown();
26553 this.markdownEl.removeClass('d-none');
26554 this.editing = false;
26557 updateMarkdown : function()
26559 if (this.getValue() == '') {
26560 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26564 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26567 resizeTextArea: function () {
26570 Roo.log([sh, this.getValue().split("\n").length * 30]);
26571 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26573 setValue : function(val)
26575 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26576 if (!this.editing) {
26577 this.updateMarkdown();
26583 if (!this.editing) {
26584 this.toggleTextEdit();
26592 * @class Roo.bootstrap.Table.AbstractSelectionModel
26593 * @extends Roo.util.Observable
26594 * Abstract base class for grid SelectionModels. It provides the interface that should be
26595 * implemented by descendant classes. This class should not be directly instantiated.
26598 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26599 this.locked = false;
26600 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26604 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26605 /** @ignore Called by the grid automatically. Do not call directly. */
26606 init : function(grid){
26612 * Locks the selections.
26615 this.locked = true;
26619 * Unlocks the selections.
26621 unlock : function(){
26622 this.locked = false;
26626 * Returns true if the selections are locked.
26627 * @return {Boolean}
26629 isLocked : function(){
26630 return this.locked;
26634 initEvents : function ()
26640 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26641 * @class Roo.bootstrap.Table.RowSelectionModel
26642 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26643 * It supports multiple selections and keyboard selection/navigation.
26645 * @param {Object} config
26648 Roo.bootstrap.Table.RowSelectionModel = function(config){
26649 Roo.apply(this, config);
26650 this.selections = new Roo.util.MixedCollection(false, function(o){
26655 this.lastActive = false;
26659 * @event selectionchange
26660 * Fires when the selection changes
26661 * @param {SelectionModel} this
26663 "selectionchange" : true,
26665 * @event afterselectionchange
26666 * Fires after the selection changes (eg. by key press or clicking)
26667 * @param {SelectionModel} this
26669 "afterselectionchange" : true,
26671 * @event beforerowselect
26672 * Fires when a row is selected being selected, return false to cancel.
26673 * @param {SelectionModel} this
26674 * @param {Number} rowIndex The selected index
26675 * @param {Boolean} keepExisting False if other selections will be cleared
26677 "beforerowselect" : true,
26680 * Fires when a row is selected.
26681 * @param {SelectionModel} this
26682 * @param {Number} rowIndex The selected index
26683 * @param {Roo.data.Record} r The record
26685 "rowselect" : true,
26687 * @event rowdeselect
26688 * Fires when a row is deselected.
26689 * @param {SelectionModel} this
26690 * @param {Number} rowIndex The selected index
26692 "rowdeselect" : true
26694 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26695 this.locked = false;
26698 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26700 * @cfg {Boolean} singleSelect
26701 * True to allow selection of only one row at a time (defaults to false)
26703 singleSelect : false,
26706 initEvents : function()
26709 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26710 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26711 //}else{ // allow click to work like normal
26712 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26714 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26715 this.grid.on("rowclick", this.handleMouseDown, this);
26717 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26718 "up" : function(e){
26720 this.selectPrevious(e.shiftKey);
26721 }else if(this.last !== false && this.lastActive !== false){
26722 var last = this.last;
26723 this.selectRange(this.last, this.lastActive-1);
26724 this.grid.getView().focusRow(this.lastActive);
26725 if(last !== false){
26729 this.selectFirstRow();
26731 this.fireEvent("afterselectionchange", this);
26733 "down" : function(e){
26735 this.selectNext(e.shiftKey);
26736 }else if(this.last !== false && this.lastActive !== false){
26737 var last = this.last;
26738 this.selectRange(this.last, this.lastActive+1);
26739 this.grid.getView().focusRow(this.lastActive);
26740 if(last !== false){
26744 this.selectFirstRow();
26746 this.fireEvent("afterselectionchange", this);
26750 this.grid.store.on('load', function(){
26751 this.selections.clear();
26754 var view = this.grid.view;
26755 view.on("refresh", this.onRefresh, this);
26756 view.on("rowupdated", this.onRowUpdated, this);
26757 view.on("rowremoved", this.onRemove, this);
26762 onRefresh : function()
26764 var ds = this.grid.store, i, v = this.grid.view;
26765 var s = this.selections;
26766 s.each(function(r){
26767 if((i = ds.indexOfId(r.id)) != -1){
26776 onRemove : function(v, index, r){
26777 this.selections.remove(r);
26781 onRowUpdated : function(v, index, r){
26782 if(this.isSelected(r)){
26783 v.onRowSelect(index);
26789 * @param {Array} records The records to select
26790 * @param {Boolean} keepExisting (optional) True to keep existing selections
26792 selectRecords : function(records, keepExisting)
26795 this.clearSelections();
26797 var ds = this.grid.store;
26798 for(var i = 0, len = records.length; i < len; i++){
26799 this.selectRow(ds.indexOf(records[i]), true);
26804 * Gets the number of selected rows.
26807 getCount : function(){
26808 return this.selections.length;
26812 * Selects the first row in the grid.
26814 selectFirstRow : function(){
26819 * Select the last row.
26820 * @param {Boolean} keepExisting (optional) True to keep existing selections
26822 selectLastRow : function(keepExisting){
26823 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26824 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26828 * Selects the row immediately following the last selected row.
26829 * @param {Boolean} keepExisting (optional) True to keep existing selections
26831 selectNext : function(keepExisting)
26833 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26834 this.selectRow(this.last+1, keepExisting);
26835 this.grid.getView().focusRow(this.last);
26840 * Selects the row that precedes the last selected row.
26841 * @param {Boolean} keepExisting (optional) True to keep existing selections
26843 selectPrevious : function(keepExisting){
26845 this.selectRow(this.last-1, keepExisting);
26846 this.grid.getView().focusRow(this.last);
26851 * Returns the selected records
26852 * @return {Array} Array of selected records
26854 getSelections : function(){
26855 return [].concat(this.selections.items);
26859 * Returns the first selected record.
26862 getSelected : function(){
26863 return this.selections.itemAt(0);
26868 * Clears all selections.
26870 clearSelections : function(fast)
26876 var ds = this.grid.store;
26877 var s = this.selections;
26878 s.each(function(r){
26879 this.deselectRow(ds.indexOfId(r.id));
26883 this.selections.clear();
26890 * Selects all rows.
26892 selectAll : function(){
26896 this.selections.clear();
26897 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26898 this.selectRow(i, true);
26903 * Returns True if there is a selection.
26904 * @return {Boolean}
26906 hasSelection : function(){
26907 return this.selections.length > 0;
26911 * Returns True if the specified row is selected.
26912 * @param {Number/Record} record The record or index of the record to check
26913 * @return {Boolean}
26915 isSelected : function(index){
26916 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26917 return (r && this.selections.key(r.id) ? true : false);
26921 * Returns True if the specified record id is selected.
26922 * @param {String} id The id of record to check
26923 * @return {Boolean}
26925 isIdSelected : function(id){
26926 return (this.selections.key(id) ? true : false);
26931 handleMouseDBClick : function(e, t){
26935 handleMouseDown : function(e, t)
26937 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26938 if(this.isLocked() || rowIndex < 0 ){
26941 if(e.shiftKey && this.last !== false){
26942 var last = this.last;
26943 this.selectRange(last, rowIndex, e.ctrlKey);
26944 this.last = last; // reset the last
26948 var isSelected = this.isSelected(rowIndex);
26949 //Roo.log("select row:" + rowIndex);
26951 this.deselectRow(rowIndex);
26953 this.selectRow(rowIndex, true);
26957 if(e.button !== 0 && isSelected){
26958 alert('rowIndex 2: ' + rowIndex);
26959 view.focusRow(rowIndex);
26960 }else if(e.ctrlKey && isSelected){
26961 this.deselectRow(rowIndex);
26962 }else if(!isSelected){
26963 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26964 view.focusRow(rowIndex);
26968 this.fireEvent("afterselectionchange", this);
26971 handleDragableRowClick : function(grid, rowIndex, e)
26973 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26974 this.selectRow(rowIndex, false);
26975 grid.view.focusRow(rowIndex);
26976 this.fireEvent("afterselectionchange", this);
26981 * Selects multiple rows.
26982 * @param {Array} rows Array of the indexes of the row to select
26983 * @param {Boolean} keepExisting (optional) True to keep existing selections
26985 selectRows : function(rows, keepExisting){
26987 this.clearSelections();
26989 for(var i = 0, len = rows.length; i < len; i++){
26990 this.selectRow(rows[i], true);
26995 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26996 * @param {Number} startRow The index of the first row in the range
26997 * @param {Number} endRow The index of the last row in the range
26998 * @param {Boolean} keepExisting (optional) True to retain existing selections
27000 selectRange : function(startRow, endRow, keepExisting){
27005 this.clearSelections();
27007 if(startRow <= endRow){
27008 for(var i = startRow; i <= endRow; i++){
27009 this.selectRow(i, true);
27012 for(var i = startRow; i >= endRow; i--){
27013 this.selectRow(i, true);
27019 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27020 * @param {Number} startRow The index of the first row in the range
27021 * @param {Number} endRow The index of the last row in the range
27023 deselectRange : function(startRow, endRow, preventViewNotify){
27027 for(var i = startRow; i <= endRow; i++){
27028 this.deselectRow(i, preventViewNotify);
27034 * @param {Number} row The index of the row to select
27035 * @param {Boolean} keepExisting (optional) True to keep existing selections
27037 selectRow : function(index, keepExisting, preventViewNotify)
27039 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27042 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27043 if(!keepExisting || this.singleSelect){
27044 this.clearSelections();
27047 var r = this.grid.store.getAt(index);
27048 //console.log('selectRow - record id :' + r.id);
27050 this.selections.add(r);
27051 this.last = this.lastActive = index;
27052 if(!preventViewNotify){
27053 var proxy = new Roo.Element(
27054 this.grid.getRowDom(index)
27056 proxy.addClass('bg-info info');
27058 this.fireEvent("rowselect", this, index, r);
27059 this.fireEvent("selectionchange", this);
27065 * @param {Number} row The index of the row to deselect
27067 deselectRow : function(index, preventViewNotify)
27072 if(this.last == index){
27075 if(this.lastActive == index){
27076 this.lastActive = false;
27079 var r = this.grid.store.getAt(index);
27084 this.selections.remove(r);
27085 //.console.log('deselectRow - record id :' + r.id);
27086 if(!preventViewNotify){
27088 var proxy = new Roo.Element(
27089 this.grid.getRowDom(index)
27091 proxy.removeClass('bg-info info');
27093 this.fireEvent("rowdeselect", this, index);
27094 this.fireEvent("selectionchange", this);
27098 restoreLast : function(){
27100 this.last = this._last;
27105 acceptsNav : function(row, col, cm){
27106 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27110 onEditorKey : function(field, e){
27111 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27116 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27118 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27120 }else if(k == e.ENTER && !e.ctrlKey){
27124 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27126 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27128 }else if(k == e.ESC){
27132 g.startEditing(newCell[0], newCell[1]);
27138 * Ext JS Library 1.1.1
27139 * Copyright(c) 2006-2007, Ext JS, LLC.
27141 * Originally Released Under LGPL - original licence link has changed is not relivant.
27144 * <script type="text/javascript">
27148 * @class Roo.bootstrap.PagingToolbar
27149 * @extends Roo.bootstrap.NavSimplebar
27150 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27152 * Create a new PagingToolbar
27153 * @param {Object} config The config object
27154 * @param {Roo.data.Store} store
27156 Roo.bootstrap.PagingToolbar = function(config)
27158 // old args format still supported... - xtype is prefered..
27159 // created from xtype...
27161 this.ds = config.dataSource;
27163 if (config.store && !this.ds) {
27164 this.store= Roo.factory(config.store, Roo.data);
27165 this.ds = this.store;
27166 this.ds.xmodule = this.xmodule || false;
27169 this.toolbarItems = [];
27170 if (config.items) {
27171 this.toolbarItems = config.items;
27174 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27179 this.bind(this.ds);
27182 if (Roo.bootstrap.version == 4) {
27183 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27185 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27190 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27192 * @cfg {Roo.data.Store} dataSource
27193 * The underlying data store providing the paged data
27196 * @cfg {String/HTMLElement/Element} container
27197 * container The id or element that will contain the toolbar
27200 * @cfg {Boolean} displayInfo
27201 * True to display the displayMsg (defaults to false)
27204 * @cfg {Number} pageSize
27205 * The number of records to display per page (defaults to 20)
27209 * @cfg {String} displayMsg
27210 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27212 displayMsg : 'Displaying {0} - {1} of {2}',
27214 * @cfg {String} emptyMsg
27215 * The message to display when no records are found (defaults to "No data to display")
27217 emptyMsg : 'No data to display',
27219 * Customizable piece of the default paging text (defaults to "Page")
27222 beforePageText : "Page",
27224 * Customizable piece of the default paging text (defaults to "of %0")
27227 afterPageText : "of {0}",
27229 * Customizable piece of the default paging text (defaults to "First Page")
27232 firstText : "First Page",
27234 * Customizable piece of the default paging text (defaults to "Previous Page")
27237 prevText : "Previous Page",
27239 * Customizable piece of the default paging text (defaults to "Next Page")
27242 nextText : "Next Page",
27244 * Customizable piece of the default paging text (defaults to "Last Page")
27247 lastText : "Last Page",
27249 * Customizable piece of the default paging text (defaults to "Refresh")
27252 refreshText : "Refresh",
27256 onRender : function(ct, position)
27258 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27259 this.navgroup.parentId = this.id;
27260 this.navgroup.onRender(this.el, null);
27261 // add the buttons to the navgroup
27263 if(this.displayInfo){
27264 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27265 this.displayEl = this.el.select('.x-paging-info', true).first();
27266 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27267 // this.displayEl = navel.el.select('span',true).first();
27273 Roo.each(_this.buttons, function(e){ // this might need to use render????
27274 Roo.factory(e).render(_this.el);
27278 Roo.each(_this.toolbarItems, function(e) {
27279 _this.navgroup.addItem(e);
27283 this.first = this.navgroup.addItem({
27284 tooltip: this.firstText,
27285 cls: "prev btn-outline-secondary",
27286 html : ' <i class="fa fa-step-backward"></i>',
27288 preventDefault: true,
27289 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27292 this.prev = this.navgroup.addItem({
27293 tooltip: this.prevText,
27294 cls: "prev btn-outline-secondary",
27295 html : ' <i class="fa fa-backward"></i>',
27297 preventDefault: true,
27298 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27300 //this.addSeparator();
27303 var field = this.navgroup.addItem( {
27305 cls : 'x-paging-position btn-outline-secondary',
27307 html : this.beforePageText +
27308 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27309 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27312 this.field = field.el.select('input', true).first();
27313 this.field.on("keydown", this.onPagingKeydown, this);
27314 this.field.on("focus", function(){this.dom.select();});
27317 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27318 //this.field.setHeight(18);
27319 //this.addSeparator();
27320 this.next = this.navgroup.addItem({
27321 tooltip: this.nextText,
27322 cls: "next btn-outline-secondary",
27323 html : ' <i class="fa fa-forward"></i>',
27325 preventDefault: true,
27326 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27328 this.last = this.navgroup.addItem({
27329 tooltip: this.lastText,
27330 html : ' <i class="fa fa-step-forward"></i>',
27331 cls: "next btn-outline-secondary",
27333 preventDefault: true,
27334 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27336 //this.addSeparator();
27337 this.loading = this.navgroup.addItem({
27338 tooltip: this.refreshText,
27339 cls: "btn-outline-secondary",
27340 html : ' <i class="fa fa-refresh"></i>',
27341 preventDefault: true,
27342 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27348 updateInfo : function(){
27349 if(this.displayEl){
27350 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27351 var msg = count == 0 ?
27355 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27357 this.displayEl.update(msg);
27362 onLoad : function(ds, r, o)
27364 this.cursor = o.params && o.params.start ? o.params.start : 0;
27366 var d = this.getPageData(),
27371 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27372 this.field.dom.value = ap;
27373 this.first.setDisabled(ap == 1);
27374 this.prev.setDisabled(ap == 1);
27375 this.next.setDisabled(ap == ps);
27376 this.last.setDisabled(ap == ps);
27377 this.loading.enable();
27382 getPageData : function(){
27383 var total = this.ds.getTotalCount();
27386 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27387 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27392 onLoadError : function(){
27393 this.loading.enable();
27397 onPagingKeydown : function(e){
27398 var k = e.getKey();
27399 var d = this.getPageData();
27401 var v = this.field.dom.value, pageNum;
27402 if(!v || isNaN(pageNum = parseInt(v, 10))){
27403 this.field.dom.value = d.activePage;
27406 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27407 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27410 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))
27412 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27413 this.field.dom.value = pageNum;
27414 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27417 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27419 var v = this.field.dom.value, pageNum;
27420 var increment = (e.shiftKey) ? 10 : 1;
27421 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27424 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27425 this.field.dom.value = d.activePage;
27428 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27430 this.field.dom.value = parseInt(v, 10) + increment;
27431 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27432 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27439 beforeLoad : function(){
27441 this.loading.disable();
27446 onClick : function(which){
27455 ds.load({params:{start: 0, limit: this.pageSize}});
27458 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27461 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27464 var total = ds.getTotalCount();
27465 var extra = total % this.pageSize;
27466 var lastStart = extra ? (total - extra) : total-this.pageSize;
27467 ds.load({params:{start: lastStart, limit: this.pageSize}});
27470 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27476 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27477 * @param {Roo.data.Store} store The data store to unbind
27479 unbind : function(ds){
27480 ds.un("beforeload", this.beforeLoad, this);
27481 ds.un("load", this.onLoad, this);
27482 ds.un("loadexception", this.onLoadError, this);
27483 ds.un("remove", this.updateInfo, this);
27484 ds.un("add", this.updateInfo, this);
27485 this.ds = undefined;
27489 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27490 * @param {Roo.data.Store} store The data store to bind
27492 bind : function(ds){
27493 ds.on("beforeload", this.beforeLoad, this);
27494 ds.on("load", this.onLoad, this);
27495 ds.on("loadexception", this.onLoadError, this);
27496 ds.on("remove", this.updateInfo, this);
27497 ds.on("add", this.updateInfo, this);
27508 * @class Roo.bootstrap.MessageBar
27509 * @extends Roo.bootstrap.Component
27510 * Bootstrap MessageBar class
27511 * @cfg {String} html contents of the MessageBar
27512 * @cfg {String} weight (info | success | warning | danger) default info
27513 * @cfg {String} beforeClass insert the bar before the given class
27514 * @cfg {Boolean} closable (true | false) default false
27515 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27518 * Create a new Element
27519 * @param {Object} config The config object
27522 Roo.bootstrap.MessageBar = function(config){
27523 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27526 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27532 beforeClass: 'bootstrap-sticky-wrap',
27534 getAutoCreate : function(){
27538 cls: 'alert alert-dismissable alert-' + this.weight,
27543 html: this.html || ''
27549 cfg.cls += ' alert-messages-fixed';
27563 onRender : function(ct, position)
27565 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27568 var cfg = Roo.apply({}, this.getAutoCreate());
27572 cfg.cls += ' ' + this.cls;
27575 cfg.style = this.style;
27577 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27579 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27582 this.el.select('>button.close').on('click', this.hide, this);
27588 if (!this.rendered) {
27594 this.fireEvent('show', this);
27600 if (!this.rendered) {
27606 this.fireEvent('hide', this);
27609 update : function()
27611 // var e = this.el.dom.firstChild;
27613 // if(this.closable){
27614 // e = e.nextSibling;
27617 // e.data = this.html || '';
27619 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27635 * @class Roo.bootstrap.Graph
27636 * @extends Roo.bootstrap.Component
27637 * Bootstrap Graph class
27641 @cfg {String} graphtype bar | vbar | pie
27642 @cfg {number} g_x coodinator | centre x (pie)
27643 @cfg {number} g_y coodinator | centre y (pie)
27644 @cfg {number} g_r radius (pie)
27645 @cfg {number} g_height height of the chart (respected by all elements in the set)
27646 @cfg {number} g_width width of the chart (respected by all elements in the set)
27647 @cfg {Object} title The title of the chart
27650 -opts (object) options for the chart
27652 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27653 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27655 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.
27656 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27658 o stretch (boolean)
27660 -opts (object) options for the pie
27663 o startAngle (number)
27664 o endAngle (number)
27668 * Create a new Input
27669 * @param {Object} config The config object
27672 Roo.bootstrap.Graph = function(config){
27673 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27679 * The img click event for the img.
27680 * @param {Roo.EventObject} e
27686 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27697 //g_colors: this.colors,
27704 getAutoCreate : function(){
27715 onRender : function(ct,position){
27718 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27720 if (typeof(Raphael) == 'undefined') {
27721 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27725 this.raphael = Raphael(this.el.dom);
27727 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27728 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27729 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27730 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27732 r.text(160, 10, "Single Series Chart").attr(txtattr);
27733 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27734 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27735 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27737 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27738 r.barchart(330, 10, 300, 220, data1);
27739 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27740 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27743 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27744 // r.barchart(30, 30, 560, 250, xdata, {
27745 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27746 // axis : "0 0 1 1",
27747 // axisxlabels : xdata
27748 // //yvalues : cols,
27751 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27753 // this.load(null,xdata,{
27754 // axis : "0 0 1 1",
27755 // axisxlabels : xdata
27760 load : function(graphtype,xdata,opts)
27762 this.raphael.clear();
27764 graphtype = this.graphtype;
27769 var r = this.raphael,
27770 fin = function () {
27771 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27773 fout = function () {
27774 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27776 pfin = function() {
27777 this.sector.stop();
27778 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27781 this.label[0].stop();
27782 this.label[0].attr({ r: 7.5 });
27783 this.label[1].attr({ "font-weight": 800 });
27786 pfout = function() {
27787 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27790 this.label[0].animate({ r: 5 }, 500, "bounce");
27791 this.label[1].attr({ "font-weight": 400 });
27797 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27800 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27803 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27804 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27806 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27813 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27818 setTitle: function(o)
27823 initEvents: function() {
27826 this.el.on('click', this.onClick, this);
27830 onClick : function(e)
27832 Roo.log('img onclick');
27833 this.fireEvent('click', this, e);
27845 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27848 * @class Roo.bootstrap.dash.NumberBox
27849 * @extends Roo.bootstrap.Component
27850 * Bootstrap NumberBox class
27851 * @cfg {String} headline Box headline
27852 * @cfg {String} content Box content
27853 * @cfg {String} icon Box icon
27854 * @cfg {String} footer Footer text
27855 * @cfg {String} fhref Footer href
27858 * Create a new NumberBox
27859 * @param {Object} config The config object
27863 Roo.bootstrap.dash.NumberBox = function(config){
27864 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27868 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27877 getAutoCreate : function(){
27881 cls : 'small-box ',
27889 cls : 'roo-headline',
27890 html : this.headline
27894 cls : 'roo-content',
27895 html : this.content
27909 cls : 'ion ' + this.icon
27918 cls : 'small-box-footer',
27919 href : this.fhref || '#',
27923 cfg.cn.push(footer);
27930 onRender : function(ct,position){
27931 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27938 setHeadline: function (value)
27940 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27943 setFooter: function (value, href)
27945 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27948 this.el.select('a.small-box-footer',true).first().attr('href', href);
27953 setContent: function (value)
27955 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27958 initEvents: function()
27972 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27975 * @class Roo.bootstrap.dash.TabBox
27976 * @extends Roo.bootstrap.Component
27977 * Bootstrap TabBox class
27978 * @cfg {String} title Title of the TabBox
27979 * @cfg {String} icon Icon of the TabBox
27980 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27981 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27984 * Create a new TabBox
27985 * @param {Object} config The config object
27989 Roo.bootstrap.dash.TabBox = function(config){
27990 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27995 * When a pane is added
27996 * @param {Roo.bootstrap.dash.TabPane} pane
28000 * @event activatepane
28001 * When a pane is activated
28002 * @param {Roo.bootstrap.dash.TabPane} pane
28004 "activatepane" : true
28012 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28017 tabScrollable : false,
28019 getChildContainer : function()
28021 return this.el.select('.tab-content', true).first();
28024 getAutoCreate : function(){
28028 cls: 'pull-left header',
28036 cls: 'fa ' + this.icon
28042 cls: 'nav nav-tabs pull-right',
28048 if(this.tabScrollable){
28055 cls: 'nav nav-tabs pull-right',
28066 cls: 'nav-tabs-custom',
28071 cls: 'tab-content no-padding',
28079 initEvents : function()
28081 //Roo.log('add add pane handler');
28082 this.on('addpane', this.onAddPane, this);
28085 * Updates the box title
28086 * @param {String} html to set the title to.
28088 setTitle : function(value)
28090 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28092 onAddPane : function(pane)
28094 this.panes.push(pane);
28095 //Roo.log('addpane');
28097 // tabs are rendere left to right..
28098 if(!this.showtabs){
28102 var ctr = this.el.select('.nav-tabs', true).first();
28105 var existing = ctr.select('.nav-tab',true);
28106 var qty = existing.getCount();;
28109 var tab = ctr.createChild({
28111 cls : 'nav-tab' + (qty ? '' : ' active'),
28119 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28122 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28124 pane.el.addClass('active');
28129 onTabClick : function(ev,un,ob,pane)
28131 //Roo.log('tab - prev default');
28132 ev.preventDefault();
28135 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28136 pane.tab.addClass('active');
28137 //Roo.log(pane.title);
28138 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28139 // technically we should have a deactivate event.. but maybe add later.
28140 // and it should not de-activate the selected tab...
28141 this.fireEvent('activatepane', pane);
28142 pane.el.addClass('active');
28143 pane.fireEvent('activate');
28148 getActivePane : function()
28151 Roo.each(this.panes, function(p) {
28152 if(p.el.hasClass('active')){
28173 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28175 * @class Roo.bootstrap.TabPane
28176 * @extends Roo.bootstrap.Component
28177 * Bootstrap TabPane class
28178 * @cfg {Boolean} active (false | true) Default false
28179 * @cfg {String} title title of panel
28183 * Create a new TabPane
28184 * @param {Object} config The config object
28187 Roo.bootstrap.dash.TabPane = function(config){
28188 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28194 * When a pane is activated
28195 * @param {Roo.bootstrap.dash.TabPane} pane
28202 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28207 // the tabBox that this is attached to.
28210 getAutoCreate : function()
28218 cfg.cls += ' active';
28223 initEvents : function()
28225 //Roo.log('trigger add pane handler');
28226 this.parent().fireEvent('addpane', this)
28230 * Updates the tab title
28231 * @param {String} html to set the title to.
28233 setTitle: function(str)
28239 this.tab.select('a', true).first().dom.innerHTML = str;
28256 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28259 * @class Roo.bootstrap.menu.Menu
28260 * @extends Roo.bootstrap.Component
28261 * Bootstrap Menu class - container for Menu
28262 * @cfg {String} html Text of the menu
28263 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28264 * @cfg {String} icon Font awesome icon
28265 * @cfg {String} pos Menu align to (top | bottom) default bottom
28269 * Create a new Menu
28270 * @param {Object} config The config object
28274 Roo.bootstrap.menu.Menu = function(config){
28275 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28279 * @event beforeshow
28280 * Fires before this menu is displayed
28281 * @param {Roo.bootstrap.menu.Menu} this
28285 * @event beforehide
28286 * Fires before this menu is hidden
28287 * @param {Roo.bootstrap.menu.Menu} this
28292 * Fires after this menu is displayed
28293 * @param {Roo.bootstrap.menu.Menu} this
28298 * Fires after this menu is hidden
28299 * @param {Roo.bootstrap.menu.Menu} this
28304 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28305 * @param {Roo.bootstrap.menu.Menu} this
28306 * @param {Roo.EventObject} e
28313 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28317 weight : 'default',
28322 getChildContainer : function() {
28323 if(this.isSubMenu){
28327 return this.el.select('ul.dropdown-menu', true).first();
28330 getAutoCreate : function()
28335 cls : 'roo-menu-text',
28343 cls : 'fa ' + this.icon
28354 cls : 'dropdown-button btn btn-' + this.weight,
28359 cls : 'dropdown-toggle btn btn-' + this.weight,
28369 cls : 'dropdown-menu'
28375 if(this.pos == 'top'){
28376 cfg.cls += ' dropup';
28379 if(this.isSubMenu){
28382 cls : 'dropdown-menu'
28389 onRender : function(ct, position)
28391 this.isSubMenu = ct.hasClass('dropdown-submenu');
28393 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28396 initEvents : function()
28398 if(this.isSubMenu){
28402 this.hidden = true;
28404 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28405 this.triggerEl.on('click', this.onTriggerPress, this);
28407 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28408 this.buttonEl.on('click', this.onClick, this);
28414 if(this.isSubMenu){
28418 return this.el.select('ul.dropdown-menu', true).first();
28421 onClick : function(e)
28423 this.fireEvent("click", this, e);
28426 onTriggerPress : function(e)
28428 if (this.isVisible()) {
28435 isVisible : function(){
28436 return !this.hidden;
28441 this.fireEvent("beforeshow", this);
28443 this.hidden = false;
28444 this.el.addClass('open');
28446 Roo.get(document).on("mouseup", this.onMouseUp, this);
28448 this.fireEvent("show", this);
28455 this.fireEvent("beforehide", this);
28457 this.hidden = true;
28458 this.el.removeClass('open');
28460 Roo.get(document).un("mouseup", this.onMouseUp);
28462 this.fireEvent("hide", this);
28465 onMouseUp : function()
28479 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28482 * @class Roo.bootstrap.menu.Item
28483 * @extends Roo.bootstrap.Component
28484 * Bootstrap MenuItem class
28485 * @cfg {Boolean} submenu (true | false) default false
28486 * @cfg {String} html text of the item
28487 * @cfg {String} href the link
28488 * @cfg {Boolean} disable (true | false) default false
28489 * @cfg {Boolean} preventDefault (true | false) default true
28490 * @cfg {String} icon Font awesome icon
28491 * @cfg {String} pos Submenu align to (left | right) default right
28495 * Create a new Item
28496 * @param {Object} config The config object
28500 Roo.bootstrap.menu.Item = function(config){
28501 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28505 * Fires when the mouse is hovering over this menu
28506 * @param {Roo.bootstrap.menu.Item} this
28507 * @param {Roo.EventObject} e
28512 * Fires when the mouse exits this menu
28513 * @param {Roo.bootstrap.menu.Item} this
28514 * @param {Roo.EventObject} e
28520 * The raw click event for the entire grid.
28521 * @param {Roo.EventObject} e
28527 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28532 preventDefault: true,
28537 getAutoCreate : function()
28542 cls : 'roo-menu-item-text',
28550 cls : 'fa ' + this.icon
28559 href : this.href || '#',
28566 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28570 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28572 if(this.pos == 'left'){
28573 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28580 initEvents : function()
28582 this.el.on('mouseover', this.onMouseOver, this);
28583 this.el.on('mouseout', this.onMouseOut, this);
28585 this.el.select('a', true).first().on('click', this.onClick, this);
28589 onClick : function(e)
28591 if(this.preventDefault){
28592 e.preventDefault();
28595 this.fireEvent("click", this, e);
28598 onMouseOver : function(e)
28600 if(this.submenu && this.pos == 'left'){
28601 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28604 this.fireEvent("mouseover", this, e);
28607 onMouseOut : function(e)
28609 this.fireEvent("mouseout", this, e);
28621 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28624 * @class Roo.bootstrap.menu.Separator
28625 * @extends Roo.bootstrap.Component
28626 * Bootstrap Separator class
28629 * Create a new Separator
28630 * @param {Object} config The config object
28634 Roo.bootstrap.menu.Separator = function(config){
28635 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28638 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28640 getAutoCreate : function(){
28661 * @class Roo.bootstrap.Tooltip
28662 * Bootstrap Tooltip class
28663 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28664 * to determine which dom element triggers the tooltip.
28666 * It needs to add support for additional attributes like tooltip-position
28669 * Create a new Toolti
28670 * @param {Object} config The config object
28673 Roo.bootstrap.Tooltip = function(config){
28674 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28676 this.alignment = Roo.bootstrap.Tooltip.alignment;
28678 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28679 this.alignment = config.alignment;
28684 Roo.apply(Roo.bootstrap.Tooltip, {
28686 * @function init initialize tooltip monitoring.
28690 currentTip : false,
28691 currentRegion : false,
28697 Roo.get(document).on('mouseover', this.enter ,this);
28698 Roo.get(document).on('mouseout', this.leave, this);
28701 this.currentTip = new Roo.bootstrap.Tooltip();
28704 enter : function(ev)
28706 var dom = ev.getTarget();
28708 //Roo.log(['enter',dom]);
28709 var el = Roo.fly(dom);
28710 if (this.currentEl) {
28712 //Roo.log(this.currentEl);
28713 //Roo.log(this.currentEl.contains(dom));
28714 if (this.currentEl == el) {
28717 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28723 if (this.currentTip.el) {
28724 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28728 if(!el || el.dom == document){
28734 // you can not look for children, as if el is the body.. then everythign is the child..
28735 if (!el.attr('tooltip')) { //
28736 if (!el.select("[tooltip]").elements.length) {
28739 // is the mouse over this child...?
28740 bindEl = el.select("[tooltip]").first();
28741 var xy = ev.getXY();
28742 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28743 //Roo.log("not in region.");
28746 //Roo.log("child element over..");
28749 this.currentEl = bindEl;
28750 this.currentTip.bind(bindEl);
28751 this.currentRegion = Roo.lib.Region.getRegion(dom);
28752 this.currentTip.enter();
28755 leave : function(ev)
28757 var dom = ev.getTarget();
28758 //Roo.log(['leave',dom]);
28759 if (!this.currentEl) {
28764 if (dom != this.currentEl.dom) {
28767 var xy = ev.getXY();
28768 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28771 // only activate leave if mouse cursor is outside... bounding box..
28776 if (this.currentTip) {
28777 this.currentTip.leave();
28779 //Roo.log('clear currentEl');
28780 this.currentEl = false;
28785 'left' : ['r-l', [-2,0], 'right'],
28786 'right' : ['l-r', [2,0], 'left'],
28787 'bottom' : ['t-b', [0,2], 'top'],
28788 'top' : [ 'b-t', [0,-2], 'bottom']
28794 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28799 delay : null, // can be { show : 300 , hide: 500}
28803 hoverState : null, //???
28805 placement : 'bottom',
28809 getAutoCreate : function(){
28816 cls : 'tooltip-arrow arrow'
28819 cls : 'tooltip-inner'
28826 bind : function(el)
28831 initEvents : function()
28833 this.arrowEl = this.el.select('.arrow', true).first();
28834 this.innerEl = this.el.select('.tooltip-inner', true).first();
28837 enter : function () {
28839 if (this.timeout != null) {
28840 clearTimeout(this.timeout);
28843 this.hoverState = 'in';
28844 //Roo.log("enter - show");
28845 if (!this.delay || !this.delay.show) {
28850 this.timeout = setTimeout(function () {
28851 if (_t.hoverState == 'in') {
28854 }, this.delay.show);
28858 clearTimeout(this.timeout);
28860 this.hoverState = 'out';
28861 if (!this.delay || !this.delay.hide) {
28867 this.timeout = setTimeout(function () {
28868 //Roo.log("leave - timeout");
28870 if (_t.hoverState == 'out') {
28872 Roo.bootstrap.Tooltip.currentEl = false;
28877 show : function (msg)
28880 this.render(document.body);
28883 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28885 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28887 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28889 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28890 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28892 var placement = typeof this.placement == 'function' ?
28893 this.placement.call(this, this.el, on_el) :
28896 var autoToken = /\s?auto?\s?/i;
28897 var autoPlace = autoToken.test(placement);
28899 placement = placement.replace(autoToken, '') || 'top';
28903 //this.el.setXY([0,0]);
28905 //this.el.dom.style.display='block';
28907 //this.el.appendTo(on_el);
28909 var p = this.getPosition();
28910 var box = this.el.getBox();
28916 var align = this.alignment[placement];
28918 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28920 if(placement == 'top' || placement == 'bottom'){
28922 placement = 'right';
28925 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28926 placement = 'left';
28929 var scroll = Roo.select('body', true).first().getScroll();
28931 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28935 align = this.alignment[placement];
28937 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28941 this.el.alignTo(this.bindEl, align[0],align[1]);
28942 //var arrow = this.el.select('.arrow',true).first();
28943 //arrow.set(align[2],
28945 this.el.addClass(placement);
28946 this.el.addClass("bs-tooltip-"+ placement);
28948 this.el.addClass('in fade show');
28950 this.hoverState = null;
28952 if (this.el.hasClass('fade')) {
28967 //this.el.setXY([0,0]);
28968 this.el.removeClass(['show', 'in']);
28984 * @class Roo.bootstrap.LocationPicker
28985 * @extends Roo.bootstrap.Component
28986 * Bootstrap LocationPicker class
28987 * @cfg {Number} latitude Position when init default 0
28988 * @cfg {Number} longitude Position when init default 0
28989 * @cfg {Number} zoom default 15
28990 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28991 * @cfg {Boolean} mapTypeControl default false
28992 * @cfg {Boolean} disableDoubleClickZoom default false
28993 * @cfg {Boolean} scrollwheel default true
28994 * @cfg {Boolean} streetViewControl default false
28995 * @cfg {Number} radius default 0
28996 * @cfg {String} locationName
28997 * @cfg {Boolean} draggable default true
28998 * @cfg {Boolean} enableAutocomplete default false
28999 * @cfg {Boolean} enableReverseGeocode default true
29000 * @cfg {String} markerTitle
29003 * Create a new LocationPicker
29004 * @param {Object} config The config object
29008 Roo.bootstrap.LocationPicker = function(config){
29010 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29015 * Fires when the picker initialized.
29016 * @param {Roo.bootstrap.LocationPicker} this
29017 * @param {Google Location} location
29021 * @event positionchanged
29022 * Fires when the picker position changed.
29023 * @param {Roo.bootstrap.LocationPicker} this
29024 * @param {Google Location} location
29026 positionchanged : true,
29029 * Fires when the map resize.
29030 * @param {Roo.bootstrap.LocationPicker} this
29035 * Fires when the map show.
29036 * @param {Roo.bootstrap.LocationPicker} this
29041 * Fires when the map hide.
29042 * @param {Roo.bootstrap.LocationPicker} this
29047 * Fires when click the map.
29048 * @param {Roo.bootstrap.LocationPicker} this
29049 * @param {Map event} e
29053 * @event mapRightClick
29054 * Fires when right click the map.
29055 * @param {Roo.bootstrap.LocationPicker} this
29056 * @param {Map event} e
29058 mapRightClick : true,
29060 * @event markerClick
29061 * Fires when click the marker.
29062 * @param {Roo.bootstrap.LocationPicker} this
29063 * @param {Map event} e
29065 markerClick : true,
29067 * @event markerRightClick
29068 * Fires when right click the marker.
29069 * @param {Roo.bootstrap.LocationPicker} this
29070 * @param {Map event} e
29072 markerRightClick : true,
29074 * @event OverlayViewDraw
29075 * Fires when OverlayView Draw
29076 * @param {Roo.bootstrap.LocationPicker} this
29078 OverlayViewDraw : true,
29080 * @event OverlayViewOnAdd
29081 * Fires when OverlayView Draw
29082 * @param {Roo.bootstrap.LocationPicker} this
29084 OverlayViewOnAdd : true,
29086 * @event OverlayViewOnRemove
29087 * Fires when OverlayView Draw
29088 * @param {Roo.bootstrap.LocationPicker} this
29090 OverlayViewOnRemove : true,
29092 * @event OverlayViewShow
29093 * Fires when OverlayView Draw
29094 * @param {Roo.bootstrap.LocationPicker} this
29095 * @param {Pixel} cpx
29097 OverlayViewShow : true,
29099 * @event OverlayViewHide
29100 * Fires when OverlayView Draw
29101 * @param {Roo.bootstrap.LocationPicker} this
29103 OverlayViewHide : true,
29105 * @event loadexception
29106 * Fires when load google lib failed.
29107 * @param {Roo.bootstrap.LocationPicker} this
29109 loadexception : true
29114 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29116 gMapContext: false,
29122 mapTypeControl: false,
29123 disableDoubleClickZoom: false,
29125 streetViewControl: false,
29129 enableAutocomplete: false,
29130 enableReverseGeocode: true,
29133 getAutoCreate: function()
29138 cls: 'roo-location-picker'
29144 initEvents: function(ct, position)
29146 if(!this.el.getWidth() || this.isApplied()){
29150 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29155 initial: function()
29157 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29158 this.fireEvent('loadexception', this);
29162 if(!this.mapTypeId){
29163 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29166 this.gMapContext = this.GMapContext();
29168 this.initOverlayView();
29170 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29174 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29175 _this.setPosition(_this.gMapContext.marker.position);
29178 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29179 _this.fireEvent('mapClick', this, event);
29183 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29184 _this.fireEvent('mapRightClick', this, event);
29188 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29189 _this.fireEvent('markerClick', this, event);
29193 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29194 _this.fireEvent('markerRightClick', this, event);
29198 this.setPosition(this.gMapContext.location);
29200 this.fireEvent('initial', this, this.gMapContext.location);
29203 initOverlayView: function()
29207 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29211 _this.fireEvent('OverlayViewDraw', _this);
29216 _this.fireEvent('OverlayViewOnAdd', _this);
29219 onRemove: function()
29221 _this.fireEvent('OverlayViewOnRemove', _this);
29224 show: function(cpx)
29226 _this.fireEvent('OverlayViewShow', _this, cpx);
29231 _this.fireEvent('OverlayViewHide', _this);
29237 fromLatLngToContainerPixel: function(event)
29239 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29242 isApplied: function()
29244 return this.getGmapContext() == false ? false : true;
29247 getGmapContext: function()
29249 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29252 GMapContext: function()
29254 var position = new google.maps.LatLng(this.latitude, this.longitude);
29256 var _map = new google.maps.Map(this.el.dom, {
29259 mapTypeId: this.mapTypeId,
29260 mapTypeControl: this.mapTypeControl,
29261 disableDoubleClickZoom: this.disableDoubleClickZoom,
29262 scrollwheel: this.scrollwheel,
29263 streetViewControl: this.streetViewControl,
29264 locationName: this.locationName,
29265 draggable: this.draggable,
29266 enableAutocomplete: this.enableAutocomplete,
29267 enableReverseGeocode: this.enableReverseGeocode
29270 var _marker = new google.maps.Marker({
29271 position: position,
29273 title: this.markerTitle,
29274 draggable: this.draggable
29281 location: position,
29282 radius: this.radius,
29283 locationName: this.locationName,
29284 addressComponents: {
29285 formatted_address: null,
29286 addressLine1: null,
29287 addressLine2: null,
29289 streetNumber: null,
29293 stateOrProvince: null
29296 domContainer: this.el.dom,
29297 geodecoder: new google.maps.Geocoder()
29301 drawCircle: function(center, radius, options)
29303 if (this.gMapContext.circle != null) {
29304 this.gMapContext.circle.setMap(null);
29308 options = Roo.apply({}, options, {
29309 strokeColor: "#0000FF",
29310 strokeOpacity: .35,
29312 fillColor: "#0000FF",
29316 options.map = this.gMapContext.map;
29317 options.radius = radius;
29318 options.center = center;
29319 this.gMapContext.circle = new google.maps.Circle(options);
29320 return this.gMapContext.circle;
29326 setPosition: function(location)
29328 this.gMapContext.location = location;
29329 this.gMapContext.marker.setPosition(location);
29330 this.gMapContext.map.panTo(location);
29331 this.drawCircle(location, this.gMapContext.radius, {});
29335 if (this.gMapContext.settings.enableReverseGeocode) {
29336 this.gMapContext.geodecoder.geocode({
29337 latLng: this.gMapContext.location
29338 }, function(results, status) {
29340 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29341 _this.gMapContext.locationName = results[0].formatted_address;
29342 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29344 _this.fireEvent('positionchanged', this, location);
29351 this.fireEvent('positionchanged', this, location);
29356 google.maps.event.trigger(this.gMapContext.map, "resize");
29358 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29360 this.fireEvent('resize', this);
29363 setPositionByLatLng: function(latitude, longitude)
29365 this.setPosition(new google.maps.LatLng(latitude, longitude));
29368 getCurrentPosition: function()
29371 latitude: this.gMapContext.location.lat(),
29372 longitude: this.gMapContext.location.lng()
29376 getAddressName: function()
29378 return this.gMapContext.locationName;
29381 getAddressComponents: function()
29383 return this.gMapContext.addressComponents;
29386 address_component_from_google_geocode: function(address_components)
29390 for (var i = 0; i < address_components.length; i++) {
29391 var component = address_components[i];
29392 if (component.types.indexOf("postal_code") >= 0) {
29393 result.postalCode = component.short_name;
29394 } else if (component.types.indexOf("street_number") >= 0) {
29395 result.streetNumber = component.short_name;
29396 } else if (component.types.indexOf("route") >= 0) {
29397 result.streetName = component.short_name;
29398 } else if (component.types.indexOf("neighborhood") >= 0) {
29399 result.city = component.short_name;
29400 } else if (component.types.indexOf("locality") >= 0) {
29401 result.city = component.short_name;
29402 } else if (component.types.indexOf("sublocality") >= 0) {
29403 result.district = component.short_name;
29404 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29405 result.stateOrProvince = component.short_name;
29406 } else if (component.types.indexOf("country") >= 0) {
29407 result.country = component.short_name;
29411 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29412 result.addressLine2 = "";
29416 setZoomLevel: function(zoom)
29418 this.gMapContext.map.setZoom(zoom);
29431 this.fireEvent('show', this);
29442 this.fireEvent('hide', this);
29447 Roo.apply(Roo.bootstrap.LocationPicker, {
29449 OverlayView : function(map, options)
29451 options = options || {};
29458 * @class Roo.bootstrap.Alert
29459 * @extends Roo.bootstrap.Component
29460 * Bootstrap Alert class - shows an alert area box
29462 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29463 Enter a valid email address
29466 * @cfg {String} title The title of alert
29467 * @cfg {String} html The content of alert
29468 * @cfg {String} weight ( success | info | warning | danger )
29469 * @cfg {String} faicon font-awesomeicon
29472 * Create a new alert
29473 * @param {Object} config The config object
29477 Roo.bootstrap.Alert = function(config){
29478 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29482 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29489 getAutoCreate : function()
29498 cls : 'roo-alert-icon'
29503 cls : 'roo-alert-title',
29508 cls : 'roo-alert-text',
29515 cfg.cn[0].cls += ' fa ' + this.faicon;
29519 cfg.cls += ' alert-' + this.weight;
29525 initEvents: function()
29527 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29530 setTitle : function(str)
29532 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29535 setText : function(str)
29537 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29540 setWeight : function(weight)
29543 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29546 this.weight = weight;
29548 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29551 setIcon : function(icon)
29554 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29557 this.faicon = icon;
29559 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29580 * @class Roo.bootstrap.UploadCropbox
29581 * @extends Roo.bootstrap.Component
29582 * Bootstrap UploadCropbox class
29583 * @cfg {String} emptyText show when image has been loaded
29584 * @cfg {String} rotateNotify show when image too small to rotate
29585 * @cfg {Number} errorTimeout default 3000
29586 * @cfg {Number} minWidth default 300
29587 * @cfg {Number} minHeight default 300
29588 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29589 * @cfg {Boolean} isDocument (true|false) default false
29590 * @cfg {String} url action url
29591 * @cfg {String} paramName default 'imageUpload'
29592 * @cfg {String} method default POST
29593 * @cfg {Boolean} loadMask (true|false) default true
29594 * @cfg {Boolean} loadingText default 'Loading...'
29597 * Create a new UploadCropbox
29598 * @param {Object} config The config object
29601 Roo.bootstrap.UploadCropbox = function(config){
29602 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29606 * @event beforeselectfile
29607 * Fire before select file
29608 * @param {Roo.bootstrap.UploadCropbox} this
29610 "beforeselectfile" : true,
29613 * Fire after initEvent
29614 * @param {Roo.bootstrap.UploadCropbox} this
29619 * Fire after initEvent
29620 * @param {Roo.bootstrap.UploadCropbox} this
29621 * @param {String} data
29626 * Fire when preparing the file data
29627 * @param {Roo.bootstrap.UploadCropbox} this
29628 * @param {Object} file
29633 * Fire when get exception
29634 * @param {Roo.bootstrap.UploadCropbox} this
29635 * @param {XMLHttpRequest} xhr
29637 "exception" : true,
29639 * @event beforeloadcanvas
29640 * Fire before load the canvas
29641 * @param {Roo.bootstrap.UploadCropbox} this
29642 * @param {String} src
29644 "beforeloadcanvas" : true,
29647 * Fire when trash image
29648 * @param {Roo.bootstrap.UploadCropbox} this
29653 * Fire when download the image
29654 * @param {Roo.bootstrap.UploadCropbox} this
29658 * @event footerbuttonclick
29659 * Fire when footerbuttonclick
29660 * @param {Roo.bootstrap.UploadCropbox} this
29661 * @param {String} type
29663 "footerbuttonclick" : true,
29667 * @param {Roo.bootstrap.UploadCropbox} this
29672 * Fire when rotate the image
29673 * @param {Roo.bootstrap.UploadCropbox} this
29674 * @param {String} pos
29679 * Fire when inspect the file
29680 * @param {Roo.bootstrap.UploadCropbox} this
29681 * @param {Object} file
29686 * Fire when xhr upload the file
29687 * @param {Roo.bootstrap.UploadCropbox} this
29688 * @param {Object} data
29693 * Fire when arrange the file data
29694 * @param {Roo.bootstrap.UploadCropbox} this
29695 * @param {Object} formData
29700 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29703 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29705 emptyText : 'Click to upload image',
29706 rotateNotify : 'Image is too small to rotate',
29707 errorTimeout : 3000,
29721 cropType : 'image/jpeg',
29723 canvasLoaded : false,
29724 isDocument : false,
29726 paramName : 'imageUpload',
29728 loadingText : 'Loading...',
29731 getAutoCreate : function()
29735 cls : 'roo-upload-cropbox',
29739 cls : 'roo-upload-cropbox-selector',
29744 cls : 'roo-upload-cropbox-body',
29745 style : 'cursor:pointer',
29749 cls : 'roo-upload-cropbox-preview'
29753 cls : 'roo-upload-cropbox-thumb'
29757 cls : 'roo-upload-cropbox-empty-notify',
29758 html : this.emptyText
29762 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29763 html : this.rotateNotify
29769 cls : 'roo-upload-cropbox-footer',
29772 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29782 onRender : function(ct, position)
29784 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29786 if (this.buttons.length) {
29788 Roo.each(this.buttons, function(bb) {
29790 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29792 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29798 this.maskEl = this.el;
29802 initEvents : function()
29804 this.urlAPI = (window.createObjectURL && window) ||
29805 (window.URL && URL.revokeObjectURL && URL) ||
29806 (window.webkitURL && webkitURL);
29808 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29809 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29811 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29812 this.selectorEl.hide();
29814 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29815 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29817 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29818 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29819 this.thumbEl.hide();
29821 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29822 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29824 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29825 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29826 this.errorEl.hide();
29828 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29829 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29830 this.footerEl.hide();
29832 this.setThumbBoxSize();
29838 this.fireEvent('initial', this);
29845 window.addEventListener("resize", function() { _this.resize(); } );
29847 this.bodyEl.on('click', this.beforeSelectFile, this);
29850 this.bodyEl.on('touchstart', this.onTouchStart, this);
29851 this.bodyEl.on('touchmove', this.onTouchMove, this);
29852 this.bodyEl.on('touchend', this.onTouchEnd, this);
29856 this.bodyEl.on('mousedown', this.onMouseDown, this);
29857 this.bodyEl.on('mousemove', this.onMouseMove, this);
29858 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29859 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29860 Roo.get(document).on('mouseup', this.onMouseUp, this);
29863 this.selectorEl.on('change', this.onFileSelected, this);
29869 this.baseScale = 1;
29871 this.baseRotate = 1;
29872 this.dragable = false;
29873 this.pinching = false;
29876 this.cropData = false;
29877 this.notifyEl.dom.innerHTML = this.emptyText;
29879 this.selectorEl.dom.value = '';
29883 resize : function()
29885 if(this.fireEvent('resize', this) != false){
29886 this.setThumbBoxPosition();
29887 this.setCanvasPosition();
29891 onFooterButtonClick : function(e, el, o, type)
29894 case 'rotate-left' :
29895 this.onRotateLeft(e);
29897 case 'rotate-right' :
29898 this.onRotateRight(e);
29901 this.beforeSelectFile(e);
29916 this.fireEvent('footerbuttonclick', this, type);
29919 beforeSelectFile : function(e)
29921 e.preventDefault();
29923 if(this.fireEvent('beforeselectfile', this) != false){
29924 this.selectorEl.dom.click();
29928 onFileSelected : function(e)
29930 e.preventDefault();
29932 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29936 var file = this.selectorEl.dom.files[0];
29938 if(this.fireEvent('inspect', this, file) != false){
29939 this.prepare(file);
29944 trash : function(e)
29946 this.fireEvent('trash', this);
29949 download : function(e)
29951 this.fireEvent('download', this);
29954 loadCanvas : function(src)
29956 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29960 this.imageEl = document.createElement('img');
29964 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29966 this.imageEl.src = src;
29970 onLoadCanvas : function()
29972 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29973 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29975 this.bodyEl.un('click', this.beforeSelectFile, this);
29977 this.notifyEl.hide();
29978 this.thumbEl.show();
29979 this.footerEl.show();
29981 this.baseRotateLevel();
29983 if(this.isDocument){
29984 this.setThumbBoxSize();
29987 this.setThumbBoxPosition();
29989 this.baseScaleLevel();
29995 this.canvasLoaded = true;
29998 this.maskEl.unmask();
30003 setCanvasPosition : function()
30005 if(!this.canvasEl){
30009 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30010 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30012 this.previewEl.setLeft(pw);
30013 this.previewEl.setTop(ph);
30017 onMouseDown : function(e)
30021 this.dragable = true;
30022 this.pinching = false;
30024 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30025 this.dragable = false;
30029 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30030 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30034 onMouseMove : function(e)
30038 if(!this.canvasLoaded){
30042 if (!this.dragable){
30046 var minX = Math.ceil(this.thumbEl.getLeft(true));
30047 var minY = Math.ceil(this.thumbEl.getTop(true));
30049 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30050 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30052 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30053 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30055 x = x - this.mouseX;
30056 y = y - this.mouseY;
30058 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30059 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30061 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30062 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30064 this.previewEl.setLeft(bgX);
30065 this.previewEl.setTop(bgY);
30067 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30068 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30071 onMouseUp : function(e)
30075 this.dragable = false;
30078 onMouseWheel : function(e)
30082 this.startScale = this.scale;
30084 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30086 if(!this.zoomable()){
30087 this.scale = this.startScale;
30096 zoomable : function()
30098 var minScale = this.thumbEl.getWidth() / this.minWidth;
30100 if(this.minWidth < this.minHeight){
30101 minScale = this.thumbEl.getHeight() / this.minHeight;
30104 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30105 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30109 (this.rotate == 0 || this.rotate == 180) &&
30111 width > this.imageEl.OriginWidth ||
30112 height > this.imageEl.OriginHeight ||
30113 (width < this.minWidth && height < this.minHeight)
30121 (this.rotate == 90 || this.rotate == 270) &&
30123 width > this.imageEl.OriginWidth ||
30124 height > this.imageEl.OriginHeight ||
30125 (width < this.minHeight && height < this.minWidth)
30132 !this.isDocument &&
30133 (this.rotate == 0 || this.rotate == 180) &&
30135 width < this.minWidth ||
30136 width > this.imageEl.OriginWidth ||
30137 height < this.minHeight ||
30138 height > this.imageEl.OriginHeight
30145 !this.isDocument &&
30146 (this.rotate == 90 || this.rotate == 270) &&
30148 width < this.minHeight ||
30149 width > this.imageEl.OriginWidth ||
30150 height < this.minWidth ||
30151 height > this.imageEl.OriginHeight
30161 onRotateLeft : function(e)
30163 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30165 var minScale = this.thumbEl.getWidth() / this.minWidth;
30167 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30168 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30170 this.startScale = this.scale;
30172 while (this.getScaleLevel() < minScale){
30174 this.scale = this.scale + 1;
30176 if(!this.zoomable()){
30181 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30182 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30187 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30194 this.scale = this.startScale;
30196 this.onRotateFail();
30201 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30203 if(this.isDocument){
30204 this.setThumbBoxSize();
30205 this.setThumbBoxPosition();
30206 this.setCanvasPosition();
30211 this.fireEvent('rotate', this, 'left');
30215 onRotateRight : function(e)
30217 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30219 var minScale = this.thumbEl.getWidth() / this.minWidth;
30221 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30222 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30224 this.startScale = this.scale;
30226 while (this.getScaleLevel() < minScale){
30228 this.scale = this.scale + 1;
30230 if(!this.zoomable()){
30235 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30236 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30241 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30248 this.scale = this.startScale;
30250 this.onRotateFail();
30255 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30257 if(this.isDocument){
30258 this.setThumbBoxSize();
30259 this.setThumbBoxPosition();
30260 this.setCanvasPosition();
30265 this.fireEvent('rotate', this, 'right');
30268 onRotateFail : function()
30270 this.errorEl.show(true);
30274 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30279 this.previewEl.dom.innerHTML = '';
30281 var canvasEl = document.createElement("canvas");
30283 var contextEl = canvasEl.getContext("2d");
30285 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30286 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30287 var center = this.imageEl.OriginWidth / 2;
30289 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30290 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30291 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30292 center = this.imageEl.OriginHeight / 2;
30295 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30297 contextEl.translate(center, center);
30298 contextEl.rotate(this.rotate * Math.PI / 180);
30300 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30302 this.canvasEl = document.createElement("canvas");
30304 this.contextEl = this.canvasEl.getContext("2d");
30306 switch (this.rotate) {
30309 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30310 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30312 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30317 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30318 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30320 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30321 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);
30325 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30330 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30331 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30333 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30334 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);
30338 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);
30343 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30344 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30346 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30347 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30351 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);
30358 this.previewEl.appendChild(this.canvasEl);
30360 this.setCanvasPosition();
30365 if(!this.canvasLoaded){
30369 var imageCanvas = document.createElement("canvas");
30371 var imageContext = imageCanvas.getContext("2d");
30373 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30374 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30376 var center = imageCanvas.width / 2;
30378 imageContext.translate(center, center);
30380 imageContext.rotate(this.rotate * Math.PI / 180);
30382 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30384 var canvas = document.createElement("canvas");
30386 var context = canvas.getContext("2d");
30388 canvas.width = this.minWidth;
30389 canvas.height = this.minHeight;
30391 switch (this.rotate) {
30394 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30395 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30397 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30398 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30400 var targetWidth = this.minWidth - 2 * x;
30401 var targetHeight = this.minHeight - 2 * y;
30405 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30406 scale = targetWidth / width;
30409 if(x > 0 && y == 0){
30410 scale = targetHeight / height;
30413 if(x > 0 && y > 0){
30414 scale = targetWidth / width;
30416 if(width < height){
30417 scale = targetHeight / height;
30421 context.scale(scale, scale);
30423 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30424 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30426 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30427 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30429 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30434 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30435 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30437 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30438 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30440 var targetWidth = this.minWidth - 2 * x;
30441 var targetHeight = this.minHeight - 2 * y;
30445 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30446 scale = targetWidth / width;
30449 if(x > 0 && y == 0){
30450 scale = targetHeight / height;
30453 if(x > 0 && y > 0){
30454 scale = targetWidth / width;
30456 if(width < height){
30457 scale = targetHeight / height;
30461 context.scale(scale, scale);
30463 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30464 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30466 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30467 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30469 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30471 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30476 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30477 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30479 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30480 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30482 var targetWidth = this.minWidth - 2 * x;
30483 var targetHeight = this.minHeight - 2 * y;
30487 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30488 scale = targetWidth / width;
30491 if(x > 0 && y == 0){
30492 scale = targetHeight / height;
30495 if(x > 0 && y > 0){
30496 scale = targetWidth / width;
30498 if(width < height){
30499 scale = targetHeight / height;
30503 context.scale(scale, scale);
30505 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30506 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30508 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30509 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30511 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30512 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30514 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30519 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30520 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30522 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30523 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30525 var targetWidth = this.minWidth - 2 * x;
30526 var targetHeight = this.minHeight - 2 * y;
30530 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30531 scale = targetWidth / width;
30534 if(x > 0 && y == 0){
30535 scale = targetHeight / height;
30538 if(x > 0 && y > 0){
30539 scale = targetWidth / width;
30541 if(width < height){
30542 scale = targetHeight / height;
30546 context.scale(scale, scale);
30548 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30549 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30551 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30552 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30554 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30556 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30563 this.cropData = canvas.toDataURL(this.cropType);
30565 if(this.fireEvent('crop', this, this.cropData) !== false){
30566 this.process(this.file, this.cropData);
30573 setThumbBoxSize : function()
30577 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30578 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30579 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30581 this.minWidth = width;
30582 this.minHeight = height;
30584 if(this.rotate == 90 || this.rotate == 270){
30585 this.minWidth = height;
30586 this.minHeight = width;
30591 width = Math.ceil(this.minWidth * height / this.minHeight);
30593 if(this.minWidth > this.minHeight){
30595 height = Math.ceil(this.minHeight * width / this.minWidth);
30598 this.thumbEl.setStyle({
30599 width : width + 'px',
30600 height : height + 'px'
30607 setThumbBoxPosition : function()
30609 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30610 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30612 this.thumbEl.setLeft(x);
30613 this.thumbEl.setTop(y);
30617 baseRotateLevel : function()
30619 this.baseRotate = 1;
30622 typeof(this.exif) != 'undefined' &&
30623 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30624 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30626 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30629 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30633 baseScaleLevel : function()
30637 if(this.isDocument){
30639 if(this.baseRotate == 6 || this.baseRotate == 8){
30641 height = this.thumbEl.getHeight();
30642 this.baseScale = height / this.imageEl.OriginWidth;
30644 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30645 width = this.thumbEl.getWidth();
30646 this.baseScale = width / this.imageEl.OriginHeight;
30652 height = this.thumbEl.getHeight();
30653 this.baseScale = height / this.imageEl.OriginHeight;
30655 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30656 width = this.thumbEl.getWidth();
30657 this.baseScale = width / this.imageEl.OriginWidth;
30663 if(this.baseRotate == 6 || this.baseRotate == 8){
30665 width = this.thumbEl.getHeight();
30666 this.baseScale = width / this.imageEl.OriginHeight;
30668 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30669 height = this.thumbEl.getWidth();
30670 this.baseScale = height / this.imageEl.OriginHeight;
30673 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30674 height = this.thumbEl.getWidth();
30675 this.baseScale = height / this.imageEl.OriginHeight;
30677 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30678 width = this.thumbEl.getHeight();
30679 this.baseScale = width / this.imageEl.OriginWidth;
30686 width = this.thumbEl.getWidth();
30687 this.baseScale = width / this.imageEl.OriginWidth;
30689 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30690 height = this.thumbEl.getHeight();
30691 this.baseScale = height / this.imageEl.OriginHeight;
30694 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30696 height = this.thumbEl.getHeight();
30697 this.baseScale = height / this.imageEl.OriginHeight;
30699 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30700 width = this.thumbEl.getWidth();
30701 this.baseScale = width / this.imageEl.OriginWidth;
30709 getScaleLevel : function()
30711 return this.baseScale * Math.pow(1.1, this.scale);
30714 onTouchStart : function(e)
30716 if(!this.canvasLoaded){
30717 this.beforeSelectFile(e);
30721 var touches = e.browserEvent.touches;
30727 if(touches.length == 1){
30728 this.onMouseDown(e);
30732 if(touches.length != 2){
30738 for(var i = 0, finger; finger = touches[i]; i++){
30739 coords.push(finger.pageX, finger.pageY);
30742 var x = Math.pow(coords[0] - coords[2], 2);
30743 var y = Math.pow(coords[1] - coords[3], 2);
30745 this.startDistance = Math.sqrt(x + y);
30747 this.startScale = this.scale;
30749 this.pinching = true;
30750 this.dragable = false;
30754 onTouchMove : function(e)
30756 if(!this.pinching && !this.dragable){
30760 var touches = e.browserEvent.touches;
30767 this.onMouseMove(e);
30773 for(var i = 0, finger; finger = touches[i]; i++){
30774 coords.push(finger.pageX, finger.pageY);
30777 var x = Math.pow(coords[0] - coords[2], 2);
30778 var y = Math.pow(coords[1] - coords[3], 2);
30780 this.endDistance = Math.sqrt(x + y);
30782 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30784 if(!this.zoomable()){
30785 this.scale = this.startScale;
30793 onTouchEnd : function(e)
30795 this.pinching = false;
30796 this.dragable = false;
30800 process : function(file, crop)
30803 this.maskEl.mask(this.loadingText);
30806 this.xhr = new XMLHttpRequest();
30808 file.xhr = this.xhr;
30810 this.xhr.open(this.method, this.url, true);
30813 "Accept": "application/json",
30814 "Cache-Control": "no-cache",
30815 "X-Requested-With": "XMLHttpRequest"
30818 for (var headerName in headers) {
30819 var headerValue = headers[headerName];
30821 this.xhr.setRequestHeader(headerName, headerValue);
30827 this.xhr.onload = function()
30829 _this.xhrOnLoad(_this.xhr);
30832 this.xhr.onerror = function()
30834 _this.xhrOnError(_this.xhr);
30837 var formData = new FormData();
30839 formData.append('returnHTML', 'NO');
30842 formData.append('crop', crop);
30845 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30846 formData.append(this.paramName, file, file.name);
30849 if(typeof(file.filename) != 'undefined'){
30850 formData.append('filename', file.filename);
30853 if(typeof(file.mimetype) != 'undefined'){
30854 formData.append('mimetype', file.mimetype);
30857 if(this.fireEvent('arrange', this, formData) != false){
30858 this.xhr.send(formData);
30862 xhrOnLoad : function(xhr)
30865 this.maskEl.unmask();
30868 if (xhr.readyState !== 4) {
30869 this.fireEvent('exception', this, xhr);
30873 var response = Roo.decode(xhr.responseText);
30875 if(!response.success){
30876 this.fireEvent('exception', this, xhr);
30880 var response = Roo.decode(xhr.responseText);
30882 this.fireEvent('upload', this, response);
30886 xhrOnError : function()
30889 this.maskEl.unmask();
30892 Roo.log('xhr on error');
30894 var response = Roo.decode(xhr.responseText);
30900 prepare : function(file)
30903 this.maskEl.mask(this.loadingText);
30909 if(typeof(file) === 'string'){
30910 this.loadCanvas(file);
30914 if(!file || !this.urlAPI){
30919 this.cropType = file.type;
30923 if(this.fireEvent('prepare', this, this.file) != false){
30925 var reader = new FileReader();
30927 reader.onload = function (e) {
30928 if (e.target.error) {
30929 Roo.log(e.target.error);
30933 var buffer = e.target.result,
30934 dataView = new DataView(buffer),
30936 maxOffset = dataView.byteLength - 4,
30940 if (dataView.getUint16(0) === 0xffd8) {
30941 while (offset < maxOffset) {
30942 markerBytes = dataView.getUint16(offset);
30944 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30945 markerLength = dataView.getUint16(offset + 2) + 2;
30946 if (offset + markerLength > dataView.byteLength) {
30947 Roo.log('Invalid meta data: Invalid segment size.');
30951 if(markerBytes == 0xffe1){
30952 _this.parseExifData(
30959 offset += markerLength;
30969 var url = _this.urlAPI.createObjectURL(_this.file);
30971 _this.loadCanvas(url);
30976 reader.readAsArrayBuffer(this.file);
30982 parseExifData : function(dataView, offset, length)
30984 var tiffOffset = offset + 10,
30988 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30989 // No Exif data, might be XMP data instead
30993 // Check for the ASCII code for "Exif" (0x45786966):
30994 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30995 // No Exif data, might be XMP data instead
30998 if (tiffOffset + 8 > dataView.byteLength) {
30999 Roo.log('Invalid Exif data: Invalid segment size.');
31002 // Check for the two null bytes:
31003 if (dataView.getUint16(offset + 8) !== 0x0000) {
31004 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31007 // Check the byte alignment:
31008 switch (dataView.getUint16(tiffOffset)) {
31010 littleEndian = true;
31013 littleEndian = false;
31016 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31019 // Check for the TIFF tag marker (0x002A):
31020 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31021 Roo.log('Invalid Exif data: Missing TIFF marker.');
31024 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31025 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31027 this.parseExifTags(
31030 tiffOffset + dirOffset,
31035 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31040 if (dirOffset + 6 > dataView.byteLength) {
31041 Roo.log('Invalid Exif data: Invalid directory offset.');
31044 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31045 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31046 if (dirEndOffset + 4 > dataView.byteLength) {
31047 Roo.log('Invalid Exif data: Invalid directory size.');
31050 for (i = 0; i < tagsNumber; i += 1) {
31054 dirOffset + 2 + 12 * i, // tag offset
31058 // Return the offset to the next directory:
31059 return dataView.getUint32(dirEndOffset, littleEndian);
31062 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31064 var tag = dataView.getUint16(offset, littleEndian);
31066 this.exif[tag] = this.getExifValue(
31070 dataView.getUint16(offset + 2, littleEndian), // tag type
31071 dataView.getUint32(offset + 4, littleEndian), // tag length
31076 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31078 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31087 Roo.log('Invalid Exif data: Invalid tag type.');
31091 tagSize = tagType.size * length;
31092 // Determine if the value is contained in the dataOffset bytes,
31093 // or if the value at the dataOffset is a pointer to the actual data:
31094 dataOffset = tagSize > 4 ?
31095 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31096 if (dataOffset + tagSize > dataView.byteLength) {
31097 Roo.log('Invalid Exif data: Invalid data offset.');
31100 if (length === 1) {
31101 return tagType.getValue(dataView, dataOffset, littleEndian);
31104 for (i = 0; i < length; i += 1) {
31105 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31108 if (tagType.ascii) {
31110 // Concatenate the chars:
31111 for (i = 0; i < values.length; i += 1) {
31113 // Ignore the terminating NULL byte(s):
31114 if (c === '\u0000') {
31126 Roo.apply(Roo.bootstrap.UploadCropbox, {
31128 'Orientation': 0x0112
31132 1: 0, //'top-left',
31134 3: 180, //'bottom-right',
31135 // 4: 'bottom-left',
31137 6: 90, //'right-top',
31138 // 7: 'right-bottom',
31139 8: 270 //'left-bottom'
31143 // byte, 8-bit unsigned int:
31145 getValue: function (dataView, dataOffset) {
31146 return dataView.getUint8(dataOffset);
31150 // ascii, 8-bit byte:
31152 getValue: function (dataView, dataOffset) {
31153 return String.fromCharCode(dataView.getUint8(dataOffset));
31158 // short, 16 bit int:
31160 getValue: function (dataView, dataOffset, littleEndian) {
31161 return dataView.getUint16(dataOffset, littleEndian);
31165 // long, 32 bit int:
31167 getValue: function (dataView, dataOffset, littleEndian) {
31168 return dataView.getUint32(dataOffset, littleEndian);
31172 // rational = two long values, first is numerator, second is denominator:
31174 getValue: function (dataView, dataOffset, littleEndian) {
31175 return dataView.getUint32(dataOffset, littleEndian) /
31176 dataView.getUint32(dataOffset + 4, littleEndian);
31180 // slong, 32 bit signed int:
31182 getValue: function (dataView, dataOffset, littleEndian) {
31183 return dataView.getInt32(dataOffset, littleEndian);
31187 // srational, two slongs, first is numerator, second is denominator:
31189 getValue: function (dataView, dataOffset, littleEndian) {
31190 return dataView.getInt32(dataOffset, littleEndian) /
31191 dataView.getInt32(dataOffset + 4, littleEndian);
31201 cls : 'btn-group roo-upload-cropbox-rotate-left',
31202 action : 'rotate-left',
31206 cls : 'btn btn-default',
31207 html : '<i class="fa fa-undo"></i>'
31213 cls : 'btn-group roo-upload-cropbox-picture',
31214 action : 'picture',
31218 cls : 'btn btn-default',
31219 html : '<i class="fa fa-picture-o"></i>'
31225 cls : 'btn-group roo-upload-cropbox-rotate-right',
31226 action : 'rotate-right',
31230 cls : 'btn btn-default',
31231 html : '<i class="fa fa-repeat"></i>'
31239 cls : 'btn-group roo-upload-cropbox-rotate-left',
31240 action : 'rotate-left',
31244 cls : 'btn btn-default',
31245 html : '<i class="fa fa-undo"></i>'
31251 cls : 'btn-group roo-upload-cropbox-download',
31252 action : 'download',
31256 cls : 'btn btn-default',
31257 html : '<i class="fa fa-download"></i>'
31263 cls : 'btn-group roo-upload-cropbox-crop',
31268 cls : 'btn btn-default',
31269 html : '<i class="fa fa-crop"></i>'
31275 cls : 'btn-group roo-upload-cropbox-trash',
31280 cls : 'btn btn-default',
31281 html : '<i class="fa fa-trash"></i>'
31287 cls : 'btn-group roo-upload-cropbox-rotate-right',
31288 action : 'rotate-right',
31292 cls : 'btn btn-default',
31293 html : '<i class="fa fa-repeat"></i>'
31301 cls : 'btn-group roo-upload-cropbox-rotate-left',
31302 action : 'rotate-left',
31306 cls : 'btn btn-default',
31307 html : '<i class="fa fa-undo"></i>'
31313 cls : 'btn-group roo-upload-cropbox-rotate-right',
31314 action : 'rotate-right',
31318 cls : 'btn btn-default',
31319 html : '<i class="fa fa-repeat"></i>'
31332 * @class Roo.bootstrap.DocumentManager
31333 * @extends Roo.bootstrap.Component
31334 * Bootstrap DocumentManager class
31335 * @cfg {String} paramName default 'imageUpload'
31336 * @cfg {String} toolTipName default 'filename'
31337 * @cfg {String} method default POST
31338 * @cfg {String} url action url
31339 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31340 * @cfg {Boolean} multiple multiple upload default true
31341 * @cfg {Number} thumbSize default 300
31342 * @cfg {String} fieldLabel
31343 * @cfg {Number} labelWidth default 4
31344 * @cfg {String} labelAlign (left|top) default left
31345 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31346 * @cfg {Number} labellg set the width of label (1-12)
31347 * @cfg {Number} labelmd set the width of label (1-12)
31348 * @cfg {Number} labelsm set the width of label (1-12)
31349 * @cfg {Number} labelxs set the width of label (1-12)
31352 * Create a new DocumentManager
31353 * @param {Object} config The config object
31356 Roo.bootstrap.DocumentManager = function(config){
31357 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31360 this.delegates = [];
31365 * Fire when initial the DocumentManager
31366 * @param {Roo.bootstrap.DocumentManager} this
31371 * inspect selected file
31372 * @param {Roo.bootstrap.DocumentManager} this
31373 * @param {File} file
31378 * Fire when xhr load exception
31379 * @param {Roo.bootstrap.DocumentManager} this
31380 * @param {XMLHttpRequest} xhr
31382 "exception" : true,
31384 * @event afterupload
31385 * Fire when xhr load exception
31386 * @param {Roo.bootstrap.DocumentManager} this
31387 * @param {XMLHttpRequest} xhr
31389 "afterupload" : true,
31392 * prepare the form data
31393 * @param {Roo.bootstrap.DocumentManager} this
31394 * @param {Object} formData
31399 * Fire when remove the file
31400 * @param {Roo.bootstrap.DocumentManager} this
31401 * @param {Object} file
31406 * Fire after refresh the file
31407 * @param {Roo.bootstrap.DocumentManager} this
31412 * Fire after click the image
31413 * @param {Roo.bootstrap.DocumentManager} this
31414 * @param {Object} file
31419 * Fire when upload a image and editable set to true
31420 * @param {Roo.bootstrap.DocumentManager} this
31421 * @param {Object} file
31425 * @event beforeselectfile
31426 * Fire before select file
31427 * @param {Roo.bootstrap.DocumentManager} this
31429 "beforeselectfile" : true,
31432 * Fire before process file
31433 * @param {Roo.bootstrap.DocumentManager} this
31434 * @param {Object} file
31438 * @event previewrendered
31439 * Fire when preview rendered
31440 * @param {Roo.bootstrap.DocumentManager} this
31441 * @param {Object} file
31443 "previewrendered" : true,
31446 "previewResize" : true
31451 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31460 paramName : 'imageUpload',
31461 toolTipName : 'filename',
31464 labelAlign : 'left',
31474 getAutoCreate : function()
31476 var managerWidget = {
31478 cls : 'roo-document-manager',
31482 cls : 'roo-document-manager-selector',
31487 cls : 'roo-document-manager-uploader',
31491 cls : 'roo-document-manager-upload-btn',
31492 html : '<i class="fa fa-plus"></i>'
31503 cls : 'column col-md-12',
31508 if(this.fieldLabel.length){
31513 cls : 'column col-md-12',
31514 html : this.fieldLabel
31518 cls : 'column col-md-12',
31523 if(this.labelAlign == 'left'){
31528 html : this.fieldLabel
31537 if(this.labelWidth > 12){
31538 content[0].style = "width: " + this.labelWidth + 'px';
31541 if(this.labelWidth < 13 && this.labelmd == 0){
31542 this.labelmd = this.labelWidth;
31545 if(this.labellg > 0){
31546 content[0].cls += ' col-lg-' + this.labellg;
31547 content[1].cls += ' col-lg-' + (12 - this.labellg);
31550 if(this.labelmd > 0){
31551 content[0].cls += ' col-md-' + this.labelmd;
31552 content[1].cls += ' col-md-' + (12 - this.labelmd);
31555 if(this.labelsm > 0){
31556 content[0].cls += ' col-sm-' + this.labelsm;
31557 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31560 if(this.labelxs > 0){
31561 content[0].cls += ' col-xs-' + this.labelxs;
31562 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31570 cls : 'row clearfix',
31578 initEvents : function()
31580 this.managerEl = this.el.select('.roo-document-manager', true).first();
31581 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31583 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31584 this.selectorEl.hide();
31587 this.selectorEl.attr('multiple', 'multiple');
31590 this.selectorEl.on('change', this.onFileSelected, this);
31592 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31593 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31595 this.uploader.on('click', this.onUploaderClick, this);
31597 this.renderProgressDialog();
31601 window.addEventListener("resize", function() { _this.refresh(); } );
31603 this.fireEvent('initial', this);
31606 renderProgressDialog : function()
31610 this.progressDialog = new Roo.bootstrap.Modal({
31611 cls : 'roo-document-manager-progress-dialog',
31612 allow_close : false,
31623 btnclick : function() {
31624 _this.uploadCancel();
31630 this.progressDialog.render(Roo.get(document.body));
31632 this.progress = new Roo.bootstrap.Progress({
31633 cls : 'roo-document-manager-progress',
31638 this.progress.render(this.progressDialog.getChildContainer());
31640 this.progressBar = new Roo.bootstrap.ProgressBar({
31641 cls : 'roo-document-manager-progress-bar',
31644 aria_valuemax : 12,
31648 this.progressBar.render(this.progress.getChildContainer());
31651 onUploaderClick : function(e)
31653 e.preventDefault();
31655 if(this.fireEvent('beforeselectfile', this) != false){
31656 this.selectorEl.dom.click();
31661 onFileSelected : function(e)
31663 e.preventDefault();
31665 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31669 Roo.each(this.selectorEl.dom.files, function(file){
31670 if(this.fireEvent('inspect', this, file) != false){
31671 this.files.push(file);
31681 this.selectorEl.dom.value = '';
31683 if(!this.files || !this.files.length){
31687 if(this.boxes > 0 && this.files.length > this.boxes){
31688 this.files = this.files.slice(0, this.boxes);
31691 this.uploader.show();
31693 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31694 this.uploader.hide();
31703 Roo.each(this.files, function(file){
31705 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31706 var f = this.renderPreview(file);
31711 if(file.type.indexOf('image') != -1){
31712 this.delegates.push(
31714 _this.process(file);
31715 }).createDelegate(this)
31723 _this.process(file);
31724 }).createDelegate(this)
31729 this.files = files;
31731 this.delegates = this.delegates.concat(docs);
31733 if(!this.delegates.length){
31738 this.progressBar.aria_valuemax = this.delegates.length;
31745 arrange : function()
31747 if(!this.delegates.length){
31748 this.progressDialog.hide();
31753 var delegate = this.delegates.shift();
31755 this.progressDialog.show();
31757 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31759 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31764 refresh : function()
31766 this.uploader.show();
31768 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31769 this.uploader.hide();
31772 Roo.isTouch ? this.closable(false) : this.closable(true);
31774 this.fireEvent('refresh', this);
31777 onRemove : function(e, el, o)
31779 e.preventDefault();
31781 this.fireEvent('remove', this, o);
31785 remove : function(o)
31789 Roo.each(this.files, function(file){
31790 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31799 this.files = files;
31806 Roo.each(this.files, function(file){
31811 file.target.remove();
31820 onClick : function(e, el, o)
31822 e.preventDefault();
31824 this.fireEvent('click', this, o);
31828 closable : function(closable)
31830 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31832 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31844 xhrOnLoad : function(xhr)
31846 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31850 if (xhr.readyState !== 4) {
31852 this.fireEvent('exception', this, xhr);
31856 var response = Roo.decode(xhr.responseText);
31858 if(!response.success){
31860 this.fireEvent('exception', this, xhr);
31864 var file = this.renderPreview(response.data);
31866 this.files.push(file);
31870 this.fireEvent('afterupload', this, xhr);
31874 xhrOnError : function(xhr)
31876 Roo.log('xhr on error');
31878 var response = Roo.decode(xhr.responseText);
31885 process : function(file)
31887 if(this.fireEvent('process', this, file) !== false){
31888 if(this.editable && file.type.indexOf('image') != -1){
31889 this.fireEvent('edit', this, file);
31893 this.uploadStart(file, false);
31900 uploadStart : function(file, crop)
31902 this.xhr = new XMLHttpRequest();
31904 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31909 file.xhr = this.xhr;
31911 this.managerEl.createChild({
31913 cls : 'roo-document-manager-loading',
31917 tooltip : file.name,
31918 cls : 'roo-document-manager-thumb',
31919 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31925 this.xhr.open(this.method, this.url, true);
31928 "Accept": "application/json",
31929 "Cache-Control": "no-cache",
31930 "X-Requested-With": "XMLHttpRequest"
31933 for (var headerName in headers) {
31934 var headerValue = headers[headerName];
31936 this.xhr.setRequestHeader(headerName, headerValue);
31942 this.xhr.onload = function()
31944 _this.xhrOnLoad(_this.xhr);
31947 this.xhr.onerror = function()
31949 _this.xhrOnError(_this.xhr);
31952 var formData = new FormData();
31954 formData.append('returnHTML', 'NO');
31957 formData.append('crop', crop);
31960 formData.append(this.paramName, file, file.name);
31967 if(this.fireEvent('prepare', this, formData, options) != false){
31969 if(options.manually){
31973 this.xhr.send(formData);
31977 this.uploadCancel();
31980 uploadCancel : function()
31986 this.delegates = [];
31988 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31995 renderPreview : function(file)
31997 if(typeof(file.target) != 'undefined' && file.target){
32001 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32003 var previewEl = this.managerEl.createChild({
32005 cls : 'roo-document-manager-preview',
32009 tooltip : file[this.toolTipName],
32010 cls : 'roo-document-manager-thumb',
32011 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32016 html : '<i class="fa fa-times-circle"></i>'
32021 var close = previewEl.select('button.close', true).first();
32023 close.on('click', this.onRemove, this, file);
32025 file.target = previewEl;
32027 var image = previewEl.select('img', true).first();
32031 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32033 image.on('click', this.onClick, this, file);
32035 this.fireEvent('previewrendered', this, file);
32041 onPreviewLoad : function(file, image)
32043 if(typeof(file.target) == 'undefined' || !file.target){
32047 var width = image.dom.naturalWidth || image.dom.width;
32048 var height = image.dom.naturalHeight || image.dom.height;
32050 if(!this.previewResize) {
32054 if(width > height){
32055 file.target.addClass('wide');
32059 file.target.addClass('tall');
32064 uploadFromSource : function(file, crop)
32066 this.xhr = new XMLHttpRequest();
32068 this.managerEl.createChild({
32070 cls : 'roo-document-manager-loading',
32074 tooltip : file.name,
32075 cls : 'roo-document-manager-thumb',
32076 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32082 this.xhr.open(this.method, this.url, true);
32085 "Accept": "application/json",
32086 "Cache-Control": "no-cache",
32087 "X-Requested-With": "XMLHttpRequest"
32090 for (var headerName in headers) {
32091 var headerValue = headers[headerName];
32093 this.xhr.setRequestHeader(headerName, headerValue);
32099 this.xhr.onload = function()
32101 _this.xhrOnLoad(_this.xhr);
32104 this.xhr.onerror = function()
32106 _this.xhrOnError(_this.xhr);
32109 var formData = new FormData();
32111 formData.append('returnHTML', 'NO');
32113 formData.append('crop', crop);
32115 if(typeof(file.filename) != 'undefined'){
32116 formData.append('filename', file.filename);
32119 if(typeof(file.mimetype) != 'undefined'){
32120 formData.append('mimetype', file.mimetype);
32125 if(this.fireEvent('prepare', this, formData) != false){
32126 this.xhr.send(formData);
32136 * @class Roo.bootstrap.DocumentViewer
32137 * @extends Roo.bootstrap.Component
32138 * Bootstrap DocumentViewer class
32139 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32140 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32143 * Create a new DocumentViewer
32144 * @param {Object} config The config object
32147 Roo.bootstrap.DocumentViewer = function(config){
32148 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32153 * Fire after initEvent
32154 * @param {Roo.bootstrap.DocumentViewer} this
32160 * @param {Roo.bootstrap.DocumentViewer} this
32165 * Fire after download button
32166 * @param {Roo.bootstrap.DocumentViewer} this
32171 * Fire after trash button
32172 * @param {Roo.bootstrap.DocumentViewer} this
32179 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32181 showDownload : true,
32185 getAutoCreate : function()
32189 cls : 'roo-document-viewer',
32193 cls : 'roo-document-viewer-body',
32197 cls : 'roo-document-viewer-thumb',
32201 cls : 'roo-document-viewer-image'
32209 cls : 'roo-document-viewer-footer',
32212 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32216 cls : 'btn-group roo-document-viewer-download',
32220 cls : 'btn btn-default',
32221 html : '<i class="fa fa-download"></i>'
32227 cls : 'btn-group roo-document-viewer-trash',
32231 cls : 'btn btn-default',
32232 html : '<i class="fa fa-trash"></i>'
32245 initEvents : function()
32247 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32248 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32250 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32251 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32253 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32254 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32256 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32257 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32259 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32260 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32262 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32263 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32265 this.bodyEl.on('click', this.onClick, this);
32266 this.downloadBtn.on('click', this.onDownload, this);
32267 this.trashBtn.on('click', this.onTrash, this);
32269 this.downloadBtn.hide();
32270 this.trashBtn.hide();
32272 if(this.showDownload){
32273 this.downloadBtn.show();
32276 if(this.showTrash){
32277 this.trashBtn.show();
32280 if(!this.showDownload && !this.showTrash) {
32281 this.footerEl.hide();
32286 initial : function()
32288 this.fireEvent('initial', this);
32292 onClick : function(e)
32294 e.preventDefault();
32296 this.fireEvent('click', this);
32299 onDownload : function(e)
32301 e.preventDefault();
32303 this.fireEvent('download', this);
32306 onTrash : function(e)
32308 e.preventDefault();
32310 this.fireEvent('trash', this);
32322 * @class Roo.bootstrap.NavProgressBar
32323 * @extends Roo.bootstrap.Component
32324 * Bootstrap NavProgressBar class
32327 * Create a new nav progress bar
32328 * @param {Object} config The config object
32331 Roo.bootstrap.NavProgressBar = function(config){
32332 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32334 this.bullets = this.bullets || [];
32336 // Roo.bootstrap.NavProgressBar.register(this);
32340 * Fires when the active item changes
32341 * @param {Roo.bootstrap.NavProgressBar} this
32342 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32343 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32350 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32355 getAutoCreate : function()
32357 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32361 cls : 'roo-navigation-bar-group',
32365 cls : 'roo-navigation-top-bar'
32369 cls : 'roo-navigation-bullets-bar',
32373 cls : 'roo-navigation-bar'
32380 cls : 'roo-navigation-bottom-bar'
32390 initEvents: function()
32395 onRender : function(ct, position)
32397 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32399 if(this.bullets.length){
32400 Roo.each(this.bullets, function(b){
32409 addItem : function(cfg)
32411 var item = new Roo.bootstrap.NavProgressItem(cfg);
32413 item.parentId = this.id;
32414 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32417 var top = new Roo.bootstrap.Element({
32419 cls : 'roo-navigation-bar-text'
32422 var bottom = new Roo.bootstrap.Element({
32424 cls : 'roo-navigation-bar-text'
32427 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32428 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32430 var topText = new Roo.bootstrap.Element({
32432 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32435 var bottomText = new Roo.bootstrap.Element({
32437 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32440 topText.onRender(top.el, null);
32441 bottomText.onRender(bottom.el, null);
32444 item.bottomEl = bottom;
32447 this.barItems.push(item);
32452 getActive : function()
32454 var active = false;
32456 Roo.each(this.barItems, function(v){
32458 if (!v.isActive()) {
32470 setActiveItem : function(item)
32474 Roo.each(this.barItems, function(v){
32475 if (v.rid == item.rid) {
32479 if (v.isActive()) {
32480 v.setActive(false);
32485 item.setActive(true);
32487 this.fireEvent('changed', this, item, prev);
32490 getBarItem: function(rid)
32494 Roo.each(this.barItems, function(e) {
32495 if (e.rid != rid) {
32506 indexOfItem : function(item)
32510 Roo.each(this.barItems, function(v, i){
32512 if (v.rid != item.rid) {
32523 setActiveNext : function()
32525 var i = this.indexOfItem(this.getActive());
32527 if (i > this.barItems.length) {
32531 this.setActiveItem(this.barItems[i+1]);
32534 setActivePrev : function()
32536 var i = this.indexOfItem(this.getActive());
32542 this.setActiveItem(this.barItems[i-1]);
32545 format : function()
32547 if(!this.barItems.length){
32551 var width = 100 / this.barItems.length;
32553 Roo.each(this.barItems, function(i){
32554 i.el.setStyle('width', width + '%');
32555 i.topEl.el.setStyle('width', width + '%');
32556 i.bottomEl.el.setStyle('width', width + '%');
32565 * Nav Progress Item
32570 * @class Roo.bootstrap.NavProgressItem
32571 * @extends Roo.bootstrap.Component
32572 * Bootstrap NavProgressItem class
32573 * @cfg {String} rid the reference id
32574 * @cfg {Boolean} active (true|false) Is item active default false
32575 * @cfg {Boolean} disabled (true|false) Is item active default false
32576 * @cfg {String} html
32577 * @cfg {String} position (top|bottom) text position default bottom
32578 * @cfg {String} icon show icon instead of number
32581 * Create a new NavProgressItem
32582 * @param {Object} config The config object
32584 Roo.bootstrap.NavProgressItem = function(config){
32585 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32590 * The raw click event for the entire grid.
32591 * @param {Roo.bootstrap.NavProgressItem} this
32592 * @param {Roo.EventObject} e
32599 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32605 position : 'bottom',
32608 getAutoCreate : function()
32610 var iconCls = 'roo-navigation-bar-item-icon';
32612 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32616 cls: 'roo-navigation-bar-item',
32626 cfg.cls += ' active';
32629 cfg.cls += ' disabled';
32635 disable : function()
32637 this.setDisabled(true);
32640 enable : function()
32642 this.setDisabled(false);
32645 initEvents: function()
32647 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32649 this.iconEl.on('click', this.onClick, this);
32652 onClick : function(e)
32654 e.preventDefault();
32660 if(this.fireEvent('click', this, e) === false){
32664 this.parent().setActiveItem(this);
32667 isActive: function ()
32669 return this.active;
32672 setActive : function(state)
32674 if(this.active == state){
32678 this.active = state;
32681 this.el.addClass('active');
32685 this.el.removeClass('active');
32690 setDisabled : function(state)
32692 if(this.disabled == state){
32696 this.disabled = state;
32699 this.el.addClass('disabled');
32703 this.el.removeClass('disabled');
32706 tooltipEl : function()
32708 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32721 * @class Roo.bootstrap.FieldLabel
32722 * @extends Roo.bootstrap.Component
32723 * Bootstrap FieldLabel class
32724 * @cfg {String} html contents of the element
32725 * @cfg {String} tag tag of the element default label
32726 * @cfg {String} cls class of the element
32727 * @cfg {String} target label target
32728 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32729 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32730 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32731 * @cfg {String} iconTooltip default "This field is required"
32732 * @cfg {String} indicatorpos (left|right) default left
32735 * Create a new FieldLabel
32736 * @param {Object} config The config object
32739 Roo.bootstrap.FieldLabel = function(config){
32740 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32745 * Fires after the field has been marked as invalid.
32746 * @param {Roo.form.FieldLabel} this
32747 * @param {String} msg The validation message
32752 * Fires after the field has been validated with no errors.
32753 * @param {Roo.form.FieldLabel} this
32759 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32766 invalidClass : 'has-warning',
32767 validClass : 'has-success',
32768 iconTooltip : 'This field is required',
32769 indicatorpos : 'left',
32771 getAutoCreate : function(){
32774 if (!this.allowBlank) {
32780 cls : 'roo-bootstrap-field-label ' + this.cls,
32785 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32786 tooltip : this.iconTooltip
32795 if(this.indicatorpos == 'right'){
32798 cls : 'roo-bootstrap-field-label ' + this.cls,
32807 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32808 tooltip : this.iconTooltip
32817 initEvents: function()
32819 Roo.bootstrap.Element.superclass.initEvents.call(this);
32821 this.indicator = this.indicatorEl();
32823 if(this.indicator){
32824 this.indicator.removeClass('visible');
32825 this.indicator.addClass('invisible');
32828 Roo.bootstrap.FieldLabel.register(this);
32831 indicatorEl : function()
32833 var indicator = this.el.select('i.roo-required-indicator',true).first();
32844 * Mark this field as valid
32846 markValid : function()
32848 if(this.indicator){
32849 this.indicator.removeClass('visible');
32850 this.indicator.addClass('invisible');
32852 if (Roo.bootstrap.version == 3) {
32853 this.el.removeClass(this.invalidClass);
32854 this.el.addClass(this.validClass);
32856 this.el.removeClass('is-invalid');
32857 this.el.addClass('is-valid');
32861 this.fireEvent('valid', this);
32865 * Mark this field as invalid
32866 * @param {String} msg The validation message
32868 markInvalid : function(msg)
32870 if(this.indicator){
32871 this.indicator.removeClass('invisible');
32872 this.indicator.addClass('visible');
32874 if (Roo.bootstrap.version == 3) {
32875 this.el.removeClass(this.validClass);
32876 this.el.addClass(this.invalidClass);
32878 this.el.removeClass('is-valid');
32879 this.el.addClass('is-invalid');
32883 this.fireEvent('invalid', this, msg);
32889 Roo.apply(Roo.bootstrap.FieldLabel, {
32894 * register a FieldLabel Group
32895 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32897 register : function(label)
32899 if(this.groups.hasOwnProperty(label.target)){
32903 this.groups[label.target] = label;
32907 * fetch a FieldLabel Group based on the target
32908 * @param {string} target
32909 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32911 get: function(target) {
32912 if (typeof(this.groups[target]) == 'undefined') {
32916 return this.groups[target] ;
32925 * page DateSplitField.
32931 * @class Roo.bootstrap.DateSplitField
32932 * @extends Roo.bootstrap.Component
32933 * Bootstrap DateSplitField class
32934 * @cfg {string} fieldLabel - the label associated
32935 * @cfg {Number} labelWidth set the width of label (0-12)
32936 * @cfg {String} labelAlign (top|left)
32937 * @cfg {Boolean} dayAllowBlank (true|false) default false
32938 * @cfg {Boolean} monthAllowBlank (true|false) default false
32939 * @cfg {Boolean} yearAllowBlank (true|false) default false
32940 * @cfg {string} dayPlaceholder
32941 * @cfg {string} monthPlaceholder
32942 * @cfg {string} yearPlaceholder
32943 * @cfg {string} dayFormat default 'd'
32944 * @cfg {string} monthFormat default 'm'
32945 * @cfg {string} yearFormat default 'Y'
32946 * @cfg {Number} labellg set the width of label (1-12)
32947 * @cfg {Number} labelmd set the width of label (1-12)
32948 * @cfg {Number} labelsm set the width of label (1-12)
32949 * @cfg {Number} labelxs set the width of label (1-12)
32953 * Create a new DateSplitField
32954 * @param {Object} config The config object
32957 Roo.bootstrap.DateSplitField = function(config){
32958 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32964 * getting the data of years
32965 * @param {Roo.bootstrap.DateSplitField} this
32966 * @param {Object} years
32971 * getting the data of days
32972 * @param {Roo.bootstrap.DateSplitField} this
32973 * @param {Object} days
32978 * Fires after the field has been marked as invalid.
32979 * @param {Roo.form.Field} this
32980 * @param {String} msg The validation message
32985 * Fires after the field has been validated with no errors.
32986 * @param {Roo.form.Field} this
32992 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32995 labelAlign : 'top',
32997 dayAllowBlank : false,
32998 monthAllowBlank : false,
32999 yearAllowBlank : false,
33000 dayPlaceholder : '',
33001 monthPlaceholder : '',
33002 yearPlaceholder : '',
33006 isFormField : true,
33012 getAutoCreate : function()
33016 cls : 'row roo-date-split-field-group',
33021 cls : 'form-hidden-field roo-date-split-field-group-value',
33027 var labelCls = 'col-md-12';
33028 var contentCls = 'col-md-4';
33030 if(this.fieldLabel){
33034 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33038 html : this.fieldLabel
33043 if(this.labelAlign == 'left'){
33045 if(this.labelWidth > 12){
33046 label.style = "width: " + this.labelWidth + 'px';
33049 if(this.labelWidth < 13 && this.labelmd == 0){
33050 this.labelmd = this.labelWidth;
33053 if(this.labellg > 0){
33054 labelCls = ' col-lg-' + this.labellg;
33055 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33058 if(this.labelmd > 0){
33059 labelCls = ' col-md-' + this.labelmd;
33060 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33063 if(this.labelsm > 0){
33064 labelCls = ' col-sm-' + this.labelsm;
33065 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33068 if(this.labelxs > 0){
33069 labelCls = ' col-xs-' + this.labelxs;
33070 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33074 label.cls += ' ' + labelCls;
33076 cfg.cn.push(label);
33079 Roo.each(['day', 'month', 'year'], function(t){
33082 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33089 inputEl: function ()
33091 return this.el.select('.roo-date-split-field-group-value', true).first();
33094 onRender : function(ct, position)
33098 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33100 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33102 this.dayField = new Roo.bootstrap.ComboBox({
33103 allowBlank : this.dayAllowBlank,
33104 alwaysQuery : true,
33105 displayField : 'value',
33108 forceSelection : true,
33110 placeholder : this.dayPlaceholder,
33111 selectOnFocus : true,
33112 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33113 triggerAction : 'all',
33115 valueField : 'value',
33116 store : new Roo.data.SimpleStore({
33117 data : (function() {
33119 _this.fireEvent('days', _this, days);
33122 fields : [ 'value' ]
33125 select : function (_self, record, index)
33127 _this.setValue(_this.getValue());
33132 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33134 this.monthField = new Roo.bootstrap.MonthField({
33135 after : '<i class=\"fa fa-calendar\"></i>',
33136 allowBlank : this.monthAllowBlank,
33137 placeholder : this.monthPlaceholder,
33140 render : function (_self)
33142 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33143 e.preventDefault();
33147 select : function (_self, oldvalue, newvalue)
33149 _this.setValue(_this.getValue());
33154 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33156 this.yearField = new Roo.bootstrap.ComboBox({
33157 allowBlank : this.yearAllowBlank,
33158 alwaysQuery : true,
33159 displayField : 'value',
33162 forceSelection : true,
33164 placeholder : this.yearPlaceholder,
33165 selectOnFocus : true,
33166 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33167 triggerAction : 'all',
33169 valueField : 'value',
33170 store : new Roo.data.SimpleStore({
33171 data : (function() {
33173 _this.fireEvent('years', _this, years);
33176 fields : [ 'value' ]
33179 select : function (_self, record, index)
33181 _this.setValue(_this.getValue());
33186 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33189 setValue : function(v, format)
33191 this.inputEl.dom.value = v;
33193 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33195 var d = Date.parseDate(v, f);
33202 this.setDay(d.format(this.dayFormat));
33203 this.setMonth(d.format(this.monthFormat));
33204 this.setYear(d.format(this.yearFormat));
33211 setDay : function(v)
33213 this.dayField.setValue(v);
33214 this.inputEl.dom.value = this.getValue();
33219 setMonth : function(v)
33221 this.monthField.setValue(v, true);
33222 this.inputEl.dom.value = this.getValue();
33227 setYear : function(v)
33229 this.yearField.setValue(v);
33230 this.inputEl.dom.value = this.getValue();
33235 getDay : function()
33237 return this.dayField.getValue();
33240 getMonth : function()
33242 return this.monthField.getValue();
33245 getYear : function()
33247 return this.yearField.getValue();
33250 getValue : function()
33252 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33254 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33264 this.inputEl.dom.value = '';
33269 validate : function()
33271 var d = this.dayField.validate();
33272 var m = this.monthField.validate();
33273 var y = this.yearField.validate();
33278 (!this.dayAllowBlank && !d) ||
33279 (!this.monthAllowBlank && !m) ||
33280 (!this.yearAllowBlank && !y)
33285 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33294 this.markInvalid();
33299 markValid : function()
33302 var label = this.el.select('label', true).first();
33303 var icon = this.el.select('i.fa-star', true).first();
33309 this.fireEvent('valid', this);
33313 * Mark this field as invalid
33314 * @param {String} msg The validation message
33316 markInvalid : function(msg)
33319 var label = this.el.select('label', true).first();
33320 var icon = this.el.select('i.fa-star', true).first();
33322 if(label && !icon){
33323 this.el.select('.roo-date-split-field-label', true).createChild({
33325 cls : 'text-danger fa fa-lg fa-star',
33326 tooltip : 'This field is required',
33327 style : 'margin-right:5px;'
33331 this.fireEvent('invalid', this, msg);
33334 clearInvalid : function()
33336 var label = this.el.select('label', true).first();
33337 var icon = this.el.select('i.fa-star', true).first();
33343 this.fireEvent('valid', this);
33346 getName: function()
33356 * http://masonry.desandro.com
33358 * The idea is to render all the bricks based on vertical width...
33360 * The original code extends 'outlayer' - we might need to use that....
33366 * @class Roo.bootstrap.LayoutMasonry
33367 * @extends Roo.bootstrap.Component
33368 * Bootstrap Layout Masonry class
33371 * Create a new Element
33372 * @param {Object} config The config object
33375 Roo.bootstrap.LayoutMasonry = function(config){
33377 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33381 Roo.bootstrap.LayoutMasonry.register(this);
33387 * Fire after layout the items
33388 * @param {Roo.bootstrap.LayoutMasonry} this
33389 * @param {Roo.EventObject} e
33396 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33399 * @cfg {Boolean} isLayoutInstant = no animation?
33401 isLayoutInstant : false, // needed?
33404 * @cfg {Number} boxWidth width of the columns
33409 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33414 * @cfg {Number} padWidth padding below box..
33419 * @cfg {Number} gutter gutter width..
33424 * @cfg {Number} maxCols maximum number of columns
33430 * @cfg {Boolean} isAutoInitial defalut true
33432 isAutoInitial : true,
33437 * @cfg {Boolean} isHorizontal defalut false
33439 isHorizontal : false,
33441 currentSize : null,
33447 bricks: null, //CompositeElement
33451 _isLayoutInited : false,
33453 // isAlternative : false, // only use for vertical layout...
33456 * @cfg {Number} alternativePadWidth padding below box..
33458 alternativePadWidth : 50,
33460 selectedBrick : [],
33462 getAutoCreate : function(){
33464 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33468 cls: 'blog-masonary-wrapper ' + this.cls,
33470 cls : 'mas-boxes masonary'
33477 getChildContainer: function( )
33479 if (this.boxesEl) {
33480 return this.boxesEl;
33483 this.boxesEl = this.el.select('.mas-boxes').first();
33485 return this.boxesEl;
33489 initEvents : function()
33493 if(this.isAutoInitial){
33494 Roo.log('hook children rendered');
33495 this.on('childrenrendered', function() {
33496 Roo.log('children rendered');
33502 initial : function()
33504 this.selectedBrick = [];
33506 this.currentSize = this.el.getBox(true);
33508 Roo.EventManager.onWindowResize(this.resize, this);
33510 if(!this.isAutoInitial){
33518 //this.layout.defer(500,this);
33522 resize : function()
33524 var cs = this.el.getBox(true);
33527 this.currentSize.width == cs.width &&
33528 this.currentSize.x == cs.x &&
33529 this.currentSize.height == cs.height &&
33530 this.currentSize.y == cs.y
33532 Roo.log("no change in with or X or Y");
33536 this.currentSize = cs;
33542 layout : function()
33544 this._resetLayout();
33546 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33548 this.layoutItems( isInstant );
33550 this._isLayoutInited = true;
33552 this.fireEvent('layout', this);
33556 _resetLayout : function()
33558 if(this.isHorizontal){
33559 this.horizontalMeasureColumns();
33563 this.verticalMeasureColumns();
33567 verticalMeasureColumns : function()
33569 this.getContainerWidth();
33571 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33572 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33576 var boxWidth = this.boxWidth + this.padWidth;
33578 if(this.containerWidth < this.boxWidth){
33579 boxWidth = this.containerWidth
33582 var containerWidth = this.containerWidth;
33584 var cols = Math.floor(containerWidth / boxWidth);
33586 this.cols = Math.max( cols, 1 );
33588 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33590 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33592 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33594 this.colWidth = boxWidth + avail - this.padWidth;
33596 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33597 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33600 horizontalMeasureColumns : function()
33602 this.getContainerWidth();
33604 var boxWidth = this.boxWidth;
33606 if(this.containerWidth < boxWidth){
33607 boxWidth = this.containerWidth;
33610 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33612 this.el.setHeight(boxWidth);
33616 getContainerWidth : function()
33618 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33621 layoutItems : function( isInstant )
33623 Roo.log(this.bricks);
33625 var items = Roo.apply([], this.bricks);
33627 if(this.isHorizontal){
33628 this._horizontalLayoutItems( items , isInstant );
33632 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33633 // this._verticalAlternativeLayoutItems( items , isInstant );
33637 this._verticalLayoutItems( items , isInstant );
33641 _verticalLayoutItems : function ( items , isInstant)
33643 if ( !items || !items.length ) {
33648 ['xs', 'xs', 'xs', 'tall'],
33649 ['xs', 'xs', 'tall'],
33650 ['xs', 'xs', 'sm'],
33651 ['xs', 'xs', 'xs'],
33657 ['sm', 'xs', 'xs'],
33661 ['tall', 'xs', 'xs', 'xs'],
33662 ['tall', 'xs', 'xs'],
33674 Roo.each(items, function(item, k){
33676 switch (item.size) {
33677 // these layouts take up a full box,
33688 boxes.push([item]);
33711 var filterPattern = function(box, length)
33719 var pattern = box.slice(0, length);
33723 Roo.each(pattern, function(i){
33724 format.push(i.size);
33727 Roo.each(standard, function(s){
33729 if(String(s) != String(format)){
33738 if(!match && length == 1){
33743 filterPattern(box, length - 1);
33747 queue.push(pattern);
33749 box = box.slice(length, box.length);
33751 filterPattern(box, 4);
33757 Roo.each(boxes, function(box, k){
33763 if(box.length == 1){
33768 filterPattern(box, 4);
33772 this._processVerticalLayoutQueue( queue, isInstant );
33776 // _verticalAlternativeLayoutItems : function( items , isInstant )
33778 // if ( !items || !items.length ) {
33782 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33786 _horizontalLayoutItems : function ( items , isInstant)
33788 if ( !items || !items.length || items.length < 3) {
33794 var eItems = items.slice(0, 3);
33796 items = items.slice(3, items.length);
33799 ['xs', 'xs', 'xs', 'wide'],
33800 ['xs', 'xs', 'wide'],
33801 ['xs', 'xs', 'sm'],
33802 ['xs', 'xs', 'xs'],
33808 ['sm', 'xs', 'xs'],
33812 ['wide', 'xs', 'xs', 'xs'],
33813 ['wide', 'xs', 'xs'],
33826 Roo.each(items, function(item, k){
33828 switch (item.size) {
33839 boxes.push([item]);
33863 var filterPattern = function(box, length)
33871 var pattern = box.slice(0, length);
33875 Roo.each(pattern, function(i){
33876 format.push(i.size);
33879 Roo.each(standard, function(s){
33881 if(String(s) != String(format)){
33890 if(!match && length == 1){
33895 filterPattern(box, length - 1);
33899 queue.push(pattern);
33901 box = box.slice(length, box.length);
33903 filterPattern(box, 4);
33909 Roo.each(boxes, function(box, k){
33915 if(box.length == 1){
33920 filterPattern(box, 4);
33927 var pos = this.el.getBox(true);
33931 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33933 var hit_end = false;
33935 Roo.each(queue, function(box){
33939 Roo.each(box, function(b){
33941 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33951 Roo.each(box, function(b){
33953 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33956 mx = Math.max(mx, b.x);
33960 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33964 Roo.each(box, function(b){
33966 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33980 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33983 /** Sets position of item in DOM
33984 * @param {Element} item
33985 * @param {Number} x - horizontal position
33986 * @param {Number} y - vertical position
33987 * @param {Boolean} isInstant - disables transitions
33989 _processVerticalLayoutQueue : function( queue, isInstant )
33991 var pos = this.el.getBox(true);
33996 for (var i = 0; i < this.cols; i++){
34000 Roo.each(queue, function(box, k){
34002 var col = k % this.cols;
34004 Roo.each(box, function(b,kk){
34006 b.el.position('absolute');
34008 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34009 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34011 if(b.size == 'md-left' || b.size == 'md-right'){
34012 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34013 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34016 b.el.setWidth(width);
34017 b.el.setHeight(height);
34019 b.el.select('iframe',true).setSize(width,height);
34023 for (var i = 0; i < this.cols; i++){
34025 if(maxY[i] < maxY[col]){
34030 col = Math.min(col, i);
34034 x = pos.x + col * (this.colWidth + this.padWidth);
34038 var positions = [];
34040 switch (box.length){
34042 positions = this.getVerticalOneBoxColPositions(x, y, box);
34045 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34048 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34051 positions = this.getVerticalFourBoxColPositions(x, y, box);
34057 Roo.each(box, function(b,kk){
34059 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34061 var sz = b.el.getSize();
34063 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34071 for (var i = 0; i < this.cols; i++){
34072 mY = Math.max(mY, maxY[i]);
34075 this.el.setHeight(mY - pos.y);
34079 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34081 // var pos = this.el.getBox(true);
34084 // var maxX = pos.right;
34086 // var maxHeight = 0;
34088 // Roo.each(items, function(item, k){
34092 // item.el.position('absolute');
34094 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34096 // item.el.setWidth(width);
34098 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34100 // item.el.setHeight(height);
34103 // item.el.setXY([x, y], isInstant ? false : true);
34105 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34108 // y = y + height + this.alternativePadWidth;
34110 // maxHeight = maxHeight + height + this.alternativePadWidth;
34114 // this.el.setHeight(maxHeight);
34118 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34120 var pos = this.el.getBox(true);
34125 var maxX = pos.right;
34127 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34129 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34131 Roo.each(queue, function(box, k){
34133 Roo.each(box, function(b, kk){
34135 b.el.position('absolute');
34137 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34138 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34140 if(b.size == 'md-left' || b.size == 'md-right'){
34141 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34142 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34145 b.el.setWidth(width);
34146 b.el.setHeight(height);
34154 var positions = [];
34156 switch (box.length){
34158 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34161 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34164 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34167 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34173 Roo.each(box, function(b,kk){
34175 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34177 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34185 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34187 Roo.each(eItems, function(b,k){
34189 b.size = (k == 0) ? 'sm' : 'xs';
34190 b.x = (k == 0) ? 2 : 1;
34191 b.y = (k == 0) ? 2 : 1;
34193 b.el.position('absolute');
34195 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34197 b.el.setWidth(width);
34199 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34201 b.el.setHeight(height);
34205 var positions = [];
34208 x : maxX - this.unitWidth * 2 - this.gutter,
34213 x : maxX - this.unitWidth,
34214 y : minY + (this.unitWidth + this.gutter) * 2
34218 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34222 Roo.each(eItems, function(b,k){
34224 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34230 getVerticalOneBoxColPositions : function(x, y, box)
34234 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34236 if(box[0].size == 'md-left'){
34240 if(box[0].size == 'md-right'){
34245 x : x + (this.unitWidth + this.gutter) * rand,
34252 getVerticalTwoBoxColPositions : function(x, y, box)
34256 if(box[0].size == 'xs'){
34260 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34264 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34278 x : x + (this.unitWidth + this.gutter) * 2,
34279 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34286 getVerticalThreeBoxColPositions : function(x, y, box)
34290 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34298 x : x + (this.unitWidth + this.gutter) * 1,
34303 x : x + (this.unitWidth + this.gutter) * 2,
34311 if(box[0].size == 'xs' && box[1].size == 'xs'){
34320 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34324 x : x + (this.unitWidth + this.gutter) * 1,
34338 x : x + (this.unitWidth + this.gutter) * 2,
34343 x : x + (this.unitWidth + this.gutter) * 2,
34344 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34351 getVerticalFourBoxColPositions : function(x, y, box)
34355 if(box[0].size == 'xs'){
34364 y : y + (this.unitHeight + this.gutter) * 1
34369 y : y + (this.unitHeight + this.gutter) * 2
34373 x : x + (this.unitWidth + this.gutter) * 1,
34387 x : x + (this.unitWidth + this.gutter) * 2,
34392 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34393 y : y + (this.unitHeight + this.gutter) * 1
34397 x : x + (this.unitWidth + this.gutter) * 2,
34398 y : y + (this.unitWidth + this.gutter) * 2
34405 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34409 if(box[0].size == 'md-left'){
34411 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34418 if(box[0].size == 'md-right'){
34420 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34421 y : minY + (this.unitWidth + this.gutter) * 1
34427 var rand = Math.floor(Math.random() * (4 - box[0].y));
34430 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34431 y : minY + (this.unitWidth + this.gutter) * rand
34438 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34442 if(box[0].size == 'xs'){
34445 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34450 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34451 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34459 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34464 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34465 y : minY + (this.unitWidth + this.gutter) * 2
34472 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34476 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34479 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34484 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34485 y : minY + (this.unitWidth + this.gutter) * 1
34489 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34490 y : minY + (this.unitWidth + this.gutter) * 2
34497 if(box[0].size == 'xs' && box[1].size == 'xs'){
34500 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34505 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34510 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34511 y : minY + (this.unitWidth + this.gutter) * 1
34519 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34524 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34525 y : minY + (this.unitWidth + this.gutter) * 2
34529 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34530 y : minY + (this.unitWidth + this.gutter) * 2
34537 getHorizontalFourBoxColPositions : 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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34554 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),
34559 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34560 y : minY + (this.unitWidth + this.gutter) * 1
34568 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34573 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34574 y : minY + (this.unitWidth + this.gutter) * 2
34578 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34579 y : minY + (this.unitWidth + this.gutter) * 2
34583 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),
34584 y : minY + (this.unitWidth + this.gutter) * 2
34592 * remove a Masonry Brick
34593 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34595 removeBrick : function(brick_id)
34601 for (var i = 0; i<this.bricks.length; i++) {
34602 if (this.bricks[i].id == brick_id) {
34603 this.bricks.splice(i,1);
34604 this.el.dom.removeChild(Roo.get(brick_id).dom);
34611 * adds a Masonry Brick
34612 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34614 addBrick : function(cfg)
34616 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34617 //this.register(cn);
34618 cn.parentId = this.id;
34619 cn.render(this.el);
34624 * register a Masonry Brick
34625 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34628 register : function(brick)
34630 this.bricks.push(brick);
34631 brick.masonryId = this.id;
34635 * clear all the Masonry Brick
34637 clearAll : function()
34640 //this.getChildContainer().dom.innerHTML = "";
34641 this.el.dom.innerHTML = '';
34644 getSelected : function()
34646 if (!this.selectedBrick) {
34650 return this.selectedBrick;
34654 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34658 * register a Masonry Layout
34659 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34662 register : function(layout)
34664 this.groups[layout.id] = layout;
34667 * fetch a Masonry Layout based on the masonry layout ID
34668 * @param {string} the masonry layout to add
34669 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34672 get: function(layout_id) {
34673 if (typeof(this.groups[layout_id]) == 'undefined') {
34676 return this.groups[layout_id] ;
34688 * http://masonry.desandro.com
34690 * The idea is to render all the bricks based on vertical width...
34692 * The original code extends 'outlayer' - we might need to use that....
34698 * @class Roo.bootstrap.LayoutMasonryAuto
34699 * @extends Roo.bootstrap.Component
34700 * Bootstrap Layout Masonry class
34703 * Create a new Element
34704 * @param {Object} config The config object
34707 Roo.bootstrap.LayoutMasonryAuto = function(config){
34708 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34711 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34714 * @cfg {Boolean} isFitWidth - resize the width..
34716 isFitWidth : false, // options..
34718 * @cfg {Boolean} isOriginLeft = left align?
34720 isOriginLeft : true,
34722 * @cfg {Boolean} isOriginTop = top align?
34724 isOriginTop : false,
34726 * @cfg {Boolean} isLayoutInstant = no animation?
34728 isLayoutInstant : false, // needed?
34730 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34732 isResizingContainer : true,
34734 * @cfg {Number} columnWidth width of the columns
34740 * @cfg {Number} maxCols maximum number of columns
34745 * @cfg {Number} padHeight padding below box..
34751 * @cfg {Boolean} isAutoInitial defalut true
34754 isAutoInitial : true,
34760 initialColumnWidth : 0,
34761 currentSize : null,
34763 colYs : null, // array.
34770 bricks: null, //CompositeElement
34771 cols : 0, // array?
34772 // element : null, // wrapped now this.el
34773 _isLayoutInited : null,
34776 getAutoCreate : function(){
34780 cls: 'blog-masonary-wrapper ' + this.cls,
34782 cls : 'mas-boxes masonary'
34789 getChildContainer: function( )
34791 if (this.boxesEl) {
34792 return this.boxesEl;
34795 this.boxesEl = this.el.select('.mas-boxes').first();
34797 return this.boxesEl;
34801 initEvents : function()
34805 if(this.isAutoInitial){
34806 Roo.log('hook children rendered');
34807 this.on('childrenrendered', function() {
34808 Roo.log('children rendered');
34815 initial : function()
34817 this.reloadItems();
34819 this.currentSize = this.el.getBox(true);
34821 /// was window resize... - let's see if this works..
34822 Roo.EventManager.onWindowResize(this.resize, this);
34824 if(!this.isAutoInitial){
34829 this.layout.defer(500,this);
34832 reloadItems: function()
34834 this.bricks = this.el.select('.masonry-brick', true);
34836 this.bricks.each(function(b) {
34837 //Roo.log(b.getSize());
34838 if (!b.attr('originalwidth')) {
34839 b.attr('originalwidth', b.getSize().width);
34844 Roo.log(this.bricks.elements.length);
34847 resize : function()
34850 var cs = this.el.getBox(true);
34852 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34853 Roo.log("no change in with or X");
34856 this.currentSize = cs;
34860 layout : function()
34863 this._resetLayout();
34864 //this._manageStamps();
34866 // don't animate first layout
34867 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34868 this.layoutItems( isInstant );
34870 // flag for initalized
34871 this._isLayoutInited = true;
34874 layoutItems : function( isInstant )
34876 //var items = this._getItemsForLayout( this.items );
34877 // original code supports filtering layout items.. we just ignore it..
34879 this._layoutItems( this.bricks , isInstant );
34881 this._postLayout();
34883 _layoutItems : function ( items , isInstant)
34885 //this.fireEvent( 'layout', this, items );
34888 if ( !items || !items.elements.length ) {
34889 // no items, emit event with empty array
34894 items.each(function(item) {
34895 Roo.log("layout item");
34897 // get x/y object from method
34898 var position = this._getItemLayoutPosition( item );
34900 position.item = item;
34901 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34902 queue.push( position );
34905 this._processLayoutQueue( queue );
34907 /** Sets position of item in DOM
34908 * @param {Element} item
34909 * @param {Number} x - horizontal position
34910 * @param {Number} y - vertical position
34911 * @param {Boolean} isInstant - disables transitions
34913 _processLayoutQueue : function( queue )
34915 for ( var i=0, len = queue.length; i < len; i++ ) {
34916 var obj = queue[i];
34917 obj.item.position('absolute');
34918 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34924 * Any logic you want to do after each layout,
34925 * i.e. size the container
34927 _postLayout : function()
34929 this.resizeContainer();
34932 resizeContainer : function()
34934 if ( !this.isResizingContainer ) {
34937 var size = this._getContainerSize();
34939 this.el.setSize(size.width,size.height);
34940 this.boxesEl.setSize(size.width,size.height);
34946 _resetLayout : function()
34948 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34949 this.colWidth = this.el.getWidth();
34950 //this.gutter = this.el.getWidth();
34952 this.measureColumns();
34958 this.colYs.push( 0 );
34964 measureColumns : function()
34966 this.getContainerWidth();
34967 // if columnWidth is 0, default to outerWidth of first item
34968 if ( !this.columnWidth ) {
34969 var firstItem = this.bricks.first();
34970 Roo.log(firstItem);
34971 this.columnWidth = this.containerWidth;
34972 if (firstItem && firstItem.attr('originalwidth') ) {
34973 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34975 // columnWidth fall back to item of first element
34976 Roo.log("set column width?");
34977 this.initialColumnWidth = this.columnWidth ;
34979 // if first elem has no width, default to size of container
34984 if (this.initialColumnWidth) {
34985 this.columnWidth = this.initialColumnWidth;
34990 // column width is fixed at the top - however if container width get's smaller we should
34993 // this bit calcs how man columns..
34995 var columnWidth = this.columnWidth += this.gutter;
34997 // calculate columns
34998 var containerWidth = this.containerWidth + this.gutter;
35000 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35001 // fix rounding errors, typically with gutters
35002 var excess = columnWidth - containerWidth % columnWidth;
35005 // if overshoot is less than a pixel, round up, otherwise floor it
35006 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35007 cols = Math[ mathMethod ]( cols );
35008 this.cols = Math.max( cols, 1 );
35009 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35011 // padding positioning..
35012 var totalColWidth = this.cols * this.columnWidth;
35013 var padavail = this.containerWidth - totalColWidth;
35014 // so for 2 columns - we need 3 'pads'
35016 var padNeeded = (1+this.cols) * this.padWidth;
35018 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35020 this.columnWidth += padExtra
35021 //this.padWidth = Math.floor(padavail / ( this.cols));
35023 // adjust colum width so that padding is fixed??
35025 // we have 3 columns ... total = width * 3
35026 // we have X left over... that should be used by
35028 //if (this.expandC) {
35036 getContainerWidth : function()
35038 /* // container is parent if fit width
35039 var container = this.isFitWidth ? this.element.parentNode : this.element;
35040 // check that this.size and size are there
35041 // IE8 triggers resize on body size change, so they might not be
35043 var size = getSize( container ); //FIXME
35044 this.containerWidth = size && size.innerWidth; //FIXME
35047 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35051 _getItemLayoutPosition : function( item ) // what is item?
35053 // we resize the item to our columnWidth..
35055 item.setWidth(this.columnWidth);
35056 item.autoBoxAdjust = false;
35058 var sz = item.getSize();
35060 // how many columns does this brick span
35061 var remainder = this.containerWidth % this.columnWidth;
35063 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35064 // round if off by 1 pixel, otherwise use ceil
35065 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35066 colSpan = Math.min( colSpan, this.cols );
35068 // normally this should be '1' as we dont' currently allow multi width columns..
35070 var colGroup = this._getColGroup( colSpan );
35071 // get the minimum Y value from the columns
35072 var minimumY = Math.min.apply( Math, colGroup );
35073 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35075 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35077 // position the brick
35079 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35080 y: this.currentSize.y + minimumY + this.padHeight
35084 // apply setHeight to necessary columns
35085 var setHeight = minimumY + sz.height + this.padHeight;
35086 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35088 var setSpan = this.cols + 1 - colGroup.length;
35089 for ( var i = 0; i < setSpan; i++ ) {
35090 this.colYs[ shortColIndex + i ] = setHeight ;
35097 * @param {Number} colSpan - number of columns the element spans
35098 * @returns {Array} colGroup
35100 _getColGroup : function( colSpan )
35102 if ( colSpan < 2 ) {
35103 // if brick spans only one column, use all the column Ys
35108 // how many different places could this brick fit horizontally
35109 var groupCount = this.cols + 1 - colSpan;
35110 // for each group potential horizontal position
35111 for ( var i = 0; i < groupCount; i++ ) {
35112 // make an array of colY values for that one group
35113 var groupColYs = this.colYs.slice( i, i + colSpan );
35114 // and get the max value of the array
35115 colGroup[i] = Math.max.apply( Math, groupColYs );
35120 _manageStamp : function( stamp )
35122 var stampSize = stamp.getSize();
35123 var offset = stamp.getBox();
35124 // get the columns that this stamp affects
35125 var firstX = this.isOriginLeft ? offset.x : offset.right;
35126 var lastX = firstX + stampSize.width;
35127 var firstCol = Math.floor( firstX / this.columnWidth );
35128 firstCol = Math.max( 0, firstCol );
35130 var lastCol = Math.floor( lastX / this.columnWidth );
35131 // lastCol should not go over if multiple of columnWidth #425
35132 lastCol -= lastX % this.columnWidth ? 0 : 1;
35133 lastCol = Math.min( this.cols - 1, lastCol );
35135 // set colYs to bottom of the stamp
35136 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35139 for ( var i = firstCol; i <= lastCol; i++ ) {
35140 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35145 _getContainerSize : function()
35147 this.maxY = Math.max.apply( Math, this.colYs );
35152 if ( this.isFitWidth ) {
35153 size.width = this._getContainerFitWidth();
35159 _getContainerFitWidth : function()
35161 var unusedCols = 0;
35162 // count unused columns
35165 if ( this.colYs[i] !== 0 ) {
35170 // fit container to columns that have been used
35171 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35174 needsResizeLayout : function()
35176 var previousWidth = this.containerWidth;
35177 this.getContainerWidth();
35178 return previousWidth !== this.containerWidth;
35193 * @class Roo.bootstrap.MasonryBrick
35194 * @extends Roo.bootstrap.Component
35195 * Bootstrap MasonryBrick class
35198 * Create a new MasonryBrick
35199 * @param {Object} config The config object
35202 Roo.bootstrap.MasonryBrick = function(config){
35204 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35206 Roo.bootstrap.MasonryBrick.register(this);
35212 * When a MasonryBrick is clcik
35213 * @param {Roo.bootstrap.MasonryBrick} this
35214 * @param {Roo.EventObject} e
35220 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35223 * @cfg {String} title
35227 * @cfg {String} html
35231 * @cfg {String} bgimage
35235 * @cfg {String} videourl
35239 * @cfg {String} cls
35243 * @cfg {String} href
35247 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35252 * @cfg {String} placetitle (center|bottom)
35257 * @cfg {Boolean} isFitContainer defalut true
35259 isFitContainer : true,
35262 * @cfg {Boolean} preventDefault defalut false
35264 preventDefault : false,
35267 * @cfg {Boolean} inverse defalut false
35269 maskInverse : false,
35271 getAutoCreate : function()
35273 if(!this.isFitContainer){
35274 return this.getSplitAutoCreate();
35277 var cls = 'masonry-brick masonry-brick-full';
35279 if(this.href.length){
35280 cls += ' masonry-brick-link';
35283 if(this.bgimage.length){
35284 cls += ' masonry-brick-image';
35287 if(this.maskInverse){
35288 cls += ' mask-inverse';
35291 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35292 cls += ' enable-mask';
35296 cls += ' masonry-' + this.size + '-brick';
35299 if(this.placetitle.length){
35301 switch (this.placetitle) {
35303 cls += ' masonry-center-title';
35306 cls += ' masonry-bottom-title';
35313 if(!this.html.length && !this.bgimage.length){
35314 cls += ' masonry-center-title';
35317 if(!this.html.length && this.bgimage.length){
35318 cls += ' masonry-bottom-title';
35323 cls += ' ' + this.cls;
35327 tag: (this.href.length) ? 'a' : 'div',
35332 cls: 'masonry-brick-mask'
35336 cls: 'masonry-brick-paragraph',
35342 if(this.href.length){
35343 cfg.href = this.href;
35346 var cn = cfg.cn[1].cn;
35348 if(this.title.length){
35351 cls: 'masonry-brick-title',
35356 if(this.html.length){
35359 cls: 'masonry-brick-text',
35364 if (!this.title.length && !this.html.length) {
35365 cfg.cn[1].cls += ' hide';
35368 if(this.bgimage.length){
35371 cls: 'masonry-brick-image-view',
35376 if(this.videourl.length){
35377 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35378 // youtube support only?
35381 cls: 'masonry-brick-image-view',
35384 allowfullscreen : true
35392 getSplitAutoCreate : function()
35394 var cls = 'masonry-brick masonry-brick-split';
35396 if(this.href.length){
35397 cls += ' masonry-brick-link';
35400 if(this.bgimage.length){
35401 cls += ' masonry-brick-image';
35405 cls += ' masonry-' + this.size + '-brick';
35408 switch (this.placetitle) {
35410 cls += ' masonry-center-title';
35413 cls += ' masonry-bottom-title';
35416 if(!this.bgimage.length){
35417 cls += ' masonry-center-title';
35420 if(this.bgimage.length){
35421 cls += ' masonry-bottom-title';
35427 cls += ' ' + this.cls;
35431 tag: (this.href.length) ? 'a' : 'div',
35436 cls: 'masonry-brick-split-head',
35440 cls: 'masonry-brick-paragraph',
35447 cls: 'masonry-brick-split-body',
35453 if(this.href.length){
35454 cfg.href = this.href;
35457 if(this.title.length){
35458 cfg.cn[0].cn[0].cn.push({
35460 cls: 'masonry-brick-title',
35465 if(this.html.length){
35466 cfg.cn[1].cn.push({
35468 cls: 'masonry-brick-text',
35473 if(this.bgimage.length){
35474 cfg.cn[0].cn.push({
35476 cls: 'masonry-brick-image-view',
35481 if(this.videourl.length){
35482 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35483 // youtube support only?
35484 cfg.cn[0].cn.cn.push({
35486 cls: 'masonry-brick-image-view',
35489 allowfullscreen : true
35496 initEvents: function()
35498 switch (this.size) {
35531 this.el.on('touchstart', this.onTouchStart, this);
35532 this.el.on('touchmove', this.onTouchMove, this);
35533 this.el.on('touchend', this.onTouchEnd, this);
35534 this.el.on('contextmenu', this.onContextMenu, this);
35536 this.el.on('mouseenter' ,this.enter, this);
35537 this.el.on('mouseleave', this.leave, this);
35538 this.el.on('click', this.onClick, this);
35541 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35542 this.parent().bricks.push(this);
35547 onClick: function(e, el)
35549 var time = this.endTimer - this.startTimer;
35550 // Roo.log(e.preventDefault());
35553 e.preventDefault();
35558 if(!this.preventDefault){
35562 e.preventDefault();
35564 if (this.activeClass != '') {
35565 this.selectBrick();
35568 this.fireEvent('click', this, e);
35571 enter: function(e, el)
35573 e.preventDefault();
35575 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35579 if(this.bgimage.length && this.html.length){
35580 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35584 leave: function(e, el)
35586 e.preventDefault();
35588 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35592 if(this.bgimage.length && this.html.length){
35593 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35597 onTouchStart: function(e, el)
35599 // e.preventDefault();
35601 this.touchmoved = false;
35603 if(!this.isFitContainer){
35607 if(!this.bgimage.length || !this.html.length){
35611 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35613 this.timer = new Date().getTime();
35617 onTouchMove: function(e, el)
35619 this.touchmoved = true;
35622 onContextMenu : function(e,el)
35624 e.preventDefault();
35625 e.stopPropagation();
35629 onTouchEnd: function(e, el)
35631 // e.preventDefault();
35633 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35640 if(!this.bgimage.length || !this.html.length){
35642 if(this.href.length){
35643 window.location.href = this.href;
35649 if(!this.isFitContainer){
35653 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35655 window.location.href = this.href;
35658 //selection on single brick only
35659 selectBrick : function() {
35661 if (!this.parentId) {
35665 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35666 var index = m.selectedBrick.indexOf(this.id);
35669 m.selectedBrick.splice(index,1);
35670 this.el.removeClass(this.activeClass);
35674 for(var i = 0; i < m.selectedBrick.length; i++) {
35675 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35676 b.el.removeClass(b.activeClass);
35679 m.selectedBrick = [];
35681 m.selectedBrick.push(this.id);
35682 this.el.addClass(this.activeClass);
35686 isSelected : function(){
35687 return this.el.hasClass(this.activeClass);
35692 Roo.apply(Roo.bootstrap.MasonryBrick, {
35695 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35697 * register a Masonry Brick
35698 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35701 register : function(brick)
35703 //this.groups[brick.id] = brick;
35704 this.groups.add(brick.id, brick);
35707 * fetch a masonry brick based on the masonry brick ID
35708 * @param {string} the masonry brick to add
35709 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35712 get: function(brick_id)
35714 // if (typeof(this.groups[brick_id]) == 'undefined') {
35717 // return this.groups[brick_id] ;
35719 if(this.groups.key(brick_id)) {
35720 return this.groups.key(brick_id);
35738 * @class Roo.bootstrap.Brick
35739 * @extends Roo.bootstrap.Component
35740 * Bootstrap Brick class
35743 * Create a new Brick
35744 * @param {Object} config The config object
35747 Roo.bootstrap.Brick = function(config){
35748 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35754 * When a Brick is click
35755 * @param {Roo.bootstrap.Brick} this
35756 * @param {Roo.EventObject} e
35762 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35765 * @cfg {String} title
35769 * @cfg {String} html
35773 * @cfg {String} bgimage
35777 * @cfg {String} cls
35781 * @cfg {String} href
35785 * @cfg {String} video
35789 * @cfg {Boolean} square
35793 getAutoCreate : function()
35795 var cls = 'roo-brick';
35797 if(this.href.length){
35798 cls += ' roo-brick-link';
35801 if(this.bgimage.length){
35802 cls += ' roo-brick-image';
35805 if(!this.html.length && !this.bgimage.length){
35806 cls += ' roo-brick-center-title';
35809 if(!this.html.length && this.bgimage.length){
35810 cls += ' roo-brick-bottom-title';
35814 cls += ' ' + this.cls;
35818 tag: (this.href.length) ? 'a' : 'div',
35823 cls: 'roo-brick-paragraph',
35829 if(this.href.length){
35830 cfg.href = this.href;
35833 var cn = cfg.cn[0].cn;
35835 if(this.title.length){
35838 cls: 'roo-brick-title',
35843 if(this.html.length){
35846 cls: 'roo-brick-text',
35853 if(this.bgimage.length){
35856 cls: 'roo-brick-image-view',
35864 initEvents: function()
35866 if(this.title.length || this.html.length){
35867 this.el.on('mouseenter' ,this.enter, this);
35868 this.el.on('mouseleave', this.leave, this);
35871 Roo.EventManager.onWindowResize(this.resize, this);
35873 if(this.bgimage.length){
35874 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35875 this.imageEl.on('load', this.onImageLoad, this);
35882 onImageLoad : function()
35887 resize : function()
35889 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35891 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35893 if(this.bgimage.length){
35894 var image = this.el.select('.roo-brick-image-view', true).first();
35896 image.setWidth(paragraph.getWidth());
35899 image.setHeight(paragraph.getWidth());
35902 this.el.setHeight(image.getHeight());
35903 paragraph.setHeight(image.getHeight());
35909 enter: function(e, el)
35911 e.preventDefault();
35913 if(this.bgimage.length){
35914 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35915 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35919 leave: function(e, el)
35921 e.preventDefault();
35923 if(this.bgimage.length){
35924 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35925 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35940 * @class Roo.bootstrap.NumberField
35941 * @extends Roo.bootstrap.Input
35942 * Bootstrap NumberField class
35948 * Create a new NumberField
35949 * @param {Object} config The config object
35952 Roo.bootstrap.NumberField = function(config){
35953 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35956 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35959 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35961 allowDecimals : true,
35963 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35965 decimalSeparator : ".",
35967 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35969 decimalPrecision : 2,
35971 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35973 allowNegative : true,
35976 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35980 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35982 minValue : Number.NEGATIVE_INFINITY,
35984 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35986 maxValue : Number.MAX_VALUE,
35988 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35990 minText : "The minimum value for this field is {0}",
35992 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35994 maxText : "The maximum value for this field is {0}",
35996 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35997 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35999 nanText : "{0} is not a valid number",
36001 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36003 thousandsDelimiter : false,
36005 * @cfg {String} valueAlign alignment of value
36007 valueAlign : "left",
36009 getAutoCreate : function()
36011 var hiddenInput = {
36015 cls: 'hidden-number-input'
36019 hiddenInput.name = this.name;
36024 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36026 this.name = hiddenInput.name;
36028 if(cfg.cn.length > 0) {
36029 cfg.cn.push(hiddenInput);
36036 initEvents : function()
36038 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36040 var allowed = "0123456789";
36042 if(this.allowDecimals){
36043 allowed += this.decimalSeparator;
36046 if(this.allowNegative){
36050 if(this.thousandsDelimiter) {
36054 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36056 var keyPress = function(e){
36058 var k = e.getKey();
36060 var c = e.getCharCode();
36063 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36064 allowed.indexOf(String.fromCharCode(c)) === -1
36070 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36074 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36079 this.el.on("keypress", keyPress, this);
36082 validateValue : function(value)
36085 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36089 var num = this.parseValue(value);
36092 this.markInvalid(String.format(this.nanText, value));
36096 if(num < this.minValue){
36097 this.markInvalid(String.format(this.minText, this.minValue));
36101 if(num > this.maxValue){
36102 this.markInvalid(String.format(this.maxText, this.maxValue));
36109 getValue : function()
36111 var v = this.hiddenEl().getValue();
36113 return this.fixPrecision(this.parseValue(v));
36116 parseValue : function(value)
36118 if(this.thousandsDelimiter) {
36120 r = new RegExp(",", "g");
36121 value = value.replace(r, "");
36124 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36125 return isNaN(value) ? '' : value;
36128 fixPrecision : function(value)
36130 if(this.thousandsDelimiter) {
36132 r = new RegExp(",", "g");
36133 value = value.replace(r, "");
36136 var nan = isNaN(value);
36138 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36139 return nan ? '' : value;
36141 return parseFloat(value).toFixed(this.decimalPrecision);
36144 setValue : function(v)
36146 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36152 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36154 this.inputEl().dom.value = (v == '') ? '' :
36155 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36157 if(!this.allowZero && v === '0') {
36158 this.hiddenEl().dom.value = '';
36159 this.inputEl().dom.value = '';
36166 decimalPrecisionFcn : function(v)
36168 return Math.floor(v);
36171 beforeBlur : function()
36173 var v = this.parseValue(this.getRawValue());
36175 if(v || v === 0 || v === ''){
36180 hiddenEl : function()
36182 return this.el.select('input.hidden-number-input',true).first();
36194 * @class Roo.bootstrap.DocumentSlider
36195 * @extends Roo.bootstrap.Component
36196 * Bootstrap DocumentSlider class
36199 * Create a new DocumentViewer
36200 * @param {Object} config The config object
36203 Roo.bootstrap.DocumentSlider = function(config){
36204 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36211 * Fire after initEvent
36212 * @param {Roo.bootstrap.DocumentSlider} this
36217 * Fire after update
36218 * @param {Roo.bootstrap.DocumentSlider} this
36224 * @param {Roo.bootstrap.DocumentSlider} this
36230 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36236 getAutoCreate : function()
36240 cls : 'roo-document-slider',
36244 cls : 'roo-document-slider-header',
36248 cls : 'roo-document-slider-header-title'
36254 cls : 'roo-document-slider-body',
36258 cls : 'roo-document-slider-prev',
36262 cls : 'fa fa-chevron-left'
36268 cls : 'roo-document-slider-thumb',
36272 cls : 'roo-document-slider-image'
36278 cls : 'roo-document-slider-next',
36282 cls : 'fa fa-chevron-right'
36294 initEvents : function()
36296 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36297 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36299 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36300 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36302 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36303 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36305 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36306 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36308 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36309 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36311 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36312 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36314 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36315 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36317 this.thumbEl.on('click', this.onClick, this);
36319 this.prevIndicator.on('click', this.prev, this);
36321 this.nextIndicator.on('click', this.next, this);
36325 initial : function()
36327 if(this.files.length){
36328 this.indicator = 1;
36332 this.fireEvent('initial', this);
36335 update : function()
36337 this.imageEl.attr('src', this.files[this.indicator - 1]);
36339 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36341 this.prevIndicator.show();
36343 if(this.indicator == 1){
36344 this.prevIndicator.hide();
36347 this.nextIndicator.show();
36349 if(this.indicator == this.files.length){
36350 this.nextIndicator.hide();
36353 this.thumbEl.scrollTo('top');
36355 this.fireEvent('update', this);
36358 onClick : function(e)
36360 e.preventDefault();
36362 this.fireEvent('click', this);
36367 e.preventDefault();
36369 this.indicator = Math.max(1, this.indicator - 1);
36376 e.preventDefault();
36378 this.indicator = Math.min(this.files.length, this.indicator + 1);
36392 * @class Roo.bootstrap.RadioSet
36393 * @extends Roo.bootstrap.Input
36394 * Bootstrap RadioSet class
36395 * @cfg {String} indicatorpos (left|right) default left
36396 * @cfg {Boolean} inline (true|false) inline the element (default true)
36397 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36399 * Create a new RadioSet
36400 * @param {Object} config The config object
36403 Roo.bootstrap.RadioSet = function(config){
36405 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36409 Roo.bootstrap.RadioSet.register(this);
36414 * Fires when the element is checked or unchecked.
36415 * @param {Roo.bootstrap.RadioSet} this This radio
36416 * @param {Roo.bootstrap.Radio} item The checked item
36421 * Fires when the element is click.
36422 * @param {Roo.bootstrap.RadioSet} this This radio set
36423 * @param {Roo.bootstrap.Radio} item The checked item
36424 * @param {Roo.EventObject} e The event object
36431 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36439 indicatorpos : 'left',
36441 getAutoCreate : function()
36445 cls : 'roo-radio-set-label',
36449 html : this.fieldLabel
36453 if (Roo.bootstrap.version == 3) {
36456 if(this.indicatorpos == 'left'){
36459 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36460 tooltip : 'This field is required'
36465 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36466 tooltip : 'This field is required'
36472 cls : 'roo-radio-set-items'
36475 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36477 if (align === 'left' && this.fieldLabel.length) {
36480 cls : "roo-radio-set-right",
36486 if(this.labelWidth > 12){
36487 label.style = "width: " + this.labelWidth + 'px';
36490 if(this.labelWidth < 13 && this.labelmd == 0){
36491 this.labelmd = this.labelWidth;
36494 if(this.labellg > 0){
36495 label.cls += ' col-lg-' + this.labellg;
36496 items.cls += ' col-lg-' + (12 - this.labellg);
36499 if(this.labelmd > 0){
36500 label.cls += ' col-md-' + this.labelmd;
36501 items.cls += ' col-md-' + (12 - this.labelmd);
36504 if(this.labelsm > 0){
36505 label.cls += ' col-sm-' + this.labelsm;
36506 items.cls += ' col-sm-' + (12 - this.labelsm);
36509 if(this.labelxs > 0){
36510 label.cls += ' col-xs-' + this.labelxs;
36511 items.cls += ' col-xs-' + (12 - this.labelxs);
36517 cls : 'roo-radio-set',
36521 cls : 'roo-radio-set-input',
36524 value : this.value ? this.value : ''
36531 if(this.weight.length){
36532 cfg.cls += ' roo-radio-' + this.weight;
36536 cfg.cls += ' roo-radio-set-inline';
36540 ['xs','sm','md','lg'].map(function(size){
36541 if (settings[size]) {
36542 cfg.cls += ' col-' + size + '-' + settings[size];
36550 initEvents : function()
36552 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36553 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36555 if(!this.fieldLabel.length){
36556 this.labelEl.hide();
36559 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36560 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36562 this.indicator = this.indicatorEl();
36564 if(this.indicator){
36565 this.indicator.addClass('invisible');
36568 this.originalValue = this.getValue();
36572 inputEl: function ()
36574 return this.el.select('.roo-radio-set-input', true).first();
36577 getChildContainer : function()
36579 return this.itemsEl;
36582 register : function(item)
36584 this.radioes.push(item);
36588 validate : function()
36590 if(this.getVisibilityEl().hasClass('hidden')){
36596 Roo.each(this.radioes, function(i){
36605 if(this.allowBlank) {
36609 if(this.disabled || valid){
36614 this.markInvalid();
36619 markValid : function()
36621 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36622 this.indicatorEl().removeClass('visible');
36623 this.indicatorEl().addClass('invisible');
36627 if (Roo.bootstrap.version == 3) {
36628 this.el.removeClass([this.invalidClass, this.validClass]);
36629 this.el.addClass(this.validClass);
36631 this.el.removeClass(['is-invalid','is-valid']);
36632 this.el.addClass(['is-valid']);
36634 this.fireEvent('valid', this);
36637 markInvalid : function(msg)
36639 if(this.allowBlank || this.disabled){
36643 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36644 this.indicatorEl().removeClass('invisible');
36645 this.indicatorEl().addClass('visible');
36647 if (Roo.bootstrap.version == 3) {
36648 this.el.removeClass([this.invalidClass, this.validClass]);
36649 this.el.addClass(this.invalidClass);
36651 this.el.removeClass(['is-invalid','is-valid']);
36652 this.el.addClass(['is-invalid']);
36655 this.fireEvent('invalid', this, msg);
36659 setValue : function(v, suppressEvent)
36661 if(this.value === v){
36668 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36671 Roo.each(this.radioes, function(i){
36673 i.el.removeClass('checked');
36676 Roo.each(this.radioes, function(i){
36678 if(i.value === v || i.value.toString() === v.toString()){
36680 i.el.addClass('checked');
36682 if(suppressEvent !== true){
36683 this.fireEvent('check', this, i);
36694 clearInvalid : function(){
36696 if(!this.el || this.preventMark){
36700 this.el.removeClass([this.invalidClass]);
36702 this.fireEvent('valid', this);
36707 Roo.apply(Roo.bootstrap.RadioSet, {
36711 register : function(set)
36713 this.groups[set.name] = set;
36716 get: function(name)
36718 if (typeof(this.groups[name]) == 'undefined') {
36722 return this.groups[name] ;
36728 * Ext JS Library 1.1.1
36729 * Copyright(c) 2006-2007, Ext JS, LLC.
36731 * Originally Released Under LGPL - original licence link has changed is not relivant.
36734 * <script type="text/javascript">
36739 * @class Roo.bootstrap.SplitBar
36740 * @extends Roo.util.Observable
36741 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36745 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36746 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36747 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36748 split.minSize = 100;
36749 split.maxSize = 600;
36750 split.animate = true;
36751 split.on('moved', splitterMoved);
36754 * Create a new SplitBar
36755 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36756 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36757 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36758 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36759 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36760 position of the SplitBar).
36762 Roo.bootstrap.SplitBar = function(cfg){
36767 // dragElement : elm
36768 // resizingElement: el,
36770 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36771 // placement : Roo.bootstrap.SplitBar.LEFT ,
36772 // existingProxy ???
36775 this.el = Roo.get(cfg.dragElement, true);
36776 this.el.dom.unselectable = "on";
36778 this.resizingEl = Roo.get(cfg.resizingElement, true);
36782 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36783 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36786 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36789 * The minimum size of the resizing element. (Defaults to 0)
36795 * The maximum size of the resizing element. (Defaults to 2000)
36798 this.maxSize = 2000;
36801 * Whether to animate the transition to the new size
36804 this.animate = false;
36807 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36810 this.useShim = false;
36815 if(!cfg.existingProxy){
36817 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36819 this.proxy = Roo.get(cfg.existingProxy).dom;
36822 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36825 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36828 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36831 this.dragSpecs = {};
36834 * @private The adapter to use to positon and resize elements
36836 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36837 this.adapter.init(this);
36839 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36841 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36842 this.el.addClass("roo-splitbar-h");
36845 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36846 this.el.addClass("roo-splitbar-v");
36852 * Fires when the splitter is moved (alias for {@link #event-moved})
36853 * @param {Roo.bootstrap.SplitBar} this
36854 * @param {Number} newSize the new width or height
36859 * Fires when the splitter is moved
36860 * @param {Roo.bootstrap.SplitBar} this
36861 * @param {Number} newSize the new width or height
36865 * @event beforeresize
36866 * Fires before the splitter is dragged
36867 * @param {Roo.bootstrap.SplitBar} this
36869 "beforeresize" : true,
36871 "beforeapply" : true
36874 Roo.util.Observable.call(this);
36877 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36878 onStartProxyDrag : function(x, y){
36879 this.fireEvent("beforeresize", this);
36881 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36883 o.enableDisplayMode("block");
36884 // all splitbars share the same overlay
36885 Roo.bootstrap.SplitBar.prototype.overlay = o;
36887 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36888 this.overlay.show();
36889 Roo.get(this.proxy).setDisplayed("block");
36890 var size = this.adapter.getElementSize(this);
36891 this.activeMinSize = this.getMinimumSize();;
36892 this.activeMaxSize = this.getMaximumSize();;
36893 var c1 = size - this.activeMinSize;
36894 var c2 = Math.max(this.activeMaxSize - size, 0);
36895 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36896 this.dd.resetConstraints();
36897 this.dd.setXConstraint(
36898 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36899 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36901 this.dd.setYConstraint(0, 0);
36903 this.dd.resetConstraints();
36904 this.dd.setXConstraint(0, 0);
36905 this.dd.setYConstraint(
36906 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36907 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36910 this.dragSpecs.startSize = size;
36911 this.dragSpecs.startPoint = [x, y];
36912 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36916 * @private Called after the drag operation by the DDProxy
36918 onEndProxyDrag : function(e){
36919 Roo.get(this.proxy).setDisplayed(false);
36920 var endPoint = Roo.lib.Event.getXY(e);
36922 this.overlay.hide();
36925 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36926 newSize = this.dragSpecs.startSize +
36927 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36928 endPoint[0] - this.dragSpecs.startPoint[0] :
36929 this.dragSpecs.startPoint[0] - endPoint[0]
36932 newSize = this.dragSpecs.startSize +
36933 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36934 endPoint[1] - this.dragSpecs.startPoint[1] :
36935 this.dragSpecs.startPoint[1] - endPoint[1]
36938 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36939 if(newSize != this.dragSpecs.startSize){
36940 if(this.fireEvent('beforeapply', this, newSize) !== false){
36941 this.adapter.setElementSize(this, newSize);
36942 this.fireEvent("moved", this, newSize);
36943 this.fireEvent("resize", this, newSize);
36949 * Get the adapter this SplitBar uses
36950 * @return The adapter object
36952 getAdapter : function(){
36953 return this.adapter;
36957 * Set the adapter this SplitBar uses
36958 * @param {Object} adapter A SplitBar adapter object
36960 setAdapter : function(adapter){
36961 this.adapter = adapter;
36962 this.adapter.init(this);
36966 * Gets the minimum size for the resizing element
36967 * @return {Number} The minimum size
36969 getMinimumSize : function(){
36970 return this.minSize;
36974 * Sets the minimum size for the resizing element
36975 * @param {Number} minSize The minimum size
36977 setMinimumSize : function(minSize){
36978 this.minSize = minSize;
36982 * Gets the maximum size for the resizing element
36983 * @return {Number} The maximum size
36985 getMaximumSize : function(){
36986 return this.maxSize;
36990 * Sets the maximum size for the resizing element
36991 * @param {Number} maxSize The maximum size
36993 setMaximumSize : function(maxSize){
36994 this.maxSize = maxSize;
36998 * Sets the initialize size for the resizing element
36999 * @param {Number} size The initial size
37001 setCurrentSize : function(size){
37002 var oldAnimate = this.animate;
37003 this.animate = false;
37004 this.adapter.setElementSize(this, size);
37005 this.animate = oldAnimate;
37009 * Destroy this splitbar.
37010 * @param {Boolean} removeEl True to remove the element
37012 destroy : function(removeEl){
37014 this.shim.remove();
37017 this.proxy.parentNode.removeChild(this.proxy);
37025 * @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.
37027 Roo.bootstrap.SplitBar.createProxy = function(dir){
37028 var proxy = new Roo.Element(document.createElement("div"));
37029 proxy.unselectable();
37030 var cls = 'roo-splitbar-proxy';
37031 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37032 document.body.appendChild(proxy.dom);
37037 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37038 * Default Adapter. It assumes the splitter and resizing element are not positioned
37039 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37041 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37044 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37045 // do nothing for now
37046 init : function(s){
37050 * Called before drag operations to get the current size of the resizing element.
37051 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37053 getElementSize : function(s){
37054 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37055 return s.resizingEl.getWidth();
37057 return s.resizingEl.getHeight();
37062 * Called after drag operations to set the size of the resizing element.
37063 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37064 * @param {Number} newSize The new size to set
37065 * @param {Function} onComplete A function to be invoked when resizing is complete
37067 setElementSize : function(s, newSize, onComplete){
37068 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37070 s.resizingEl.setWidth(newSize);
37072 onComplete(s, newSize);
37075 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37080 s.resizingEl.setHeight(newSize);
37082 onComplete(s, newSize);
37085 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37092 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37093 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37094 * Adapter that moves the splitter element to align with the resized sizing element.
37095 * Used with an absolute positioned SplitBar.
37096 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37097 * document.body, make sure you assign an id to the body element.
37099 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37100 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37101 this.container = Roo.get(container);
37104 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37105 init : function(s){
37106 this.basic.init(s);
37109 getElementSize : function(s){
37110 return this.basic.getElementSize(s);
37113 setElementSize : function(s, newSize, onComplete){
37114 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37117 moveSplitter : function(s){
37118 var yes = Roo.bootstrap.SplitBar;
37119 switch(s.placement){
37121 s.el.setX(s.resizingEl.getRight());
37124 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37127 s.el.setY(s.resizingEl.getBottom());
37130 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37137 * Orientation constant - Create a vertical SplitBar
37141 Roo.bootstrap.SplitBar.VERTICAL = 1;
37144 * Orientation constant - Create a horizontal SplitBar
37148 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37151 * Placement constant - The resizing element is to the left of the splitter element
37155 Roo.bootstrap.SplitBar.LEFT = 1;
37158 * Placement constant - The resizing element is to the right of the splitter element
37162 Roo.bootstrap.SplitBar.RIGHT = 2;
37165 * Placement constant - The resizing element is positioned above the splitter element
37169 Roo.bootstrap.SplitBar.TOP = 3;
37172 * Placement constant - The resizing element is positioned under splitter element
37176 Roo.bootstrap.SplitBar.BOTTOM = 4;
37177 Roo.namespace("Roo.bootstrap.layout");/*
37179 * Ext JS Library 1.1.1
37180 * Copyright(c) 2006-2007, Ext JS, LLC.
37182 * Originally Released Under LGPL - original licence link has changed is not relivant.
37185 * <script type="text/javascript">
37189 * @class Roo.bootstrap.layout.Manager
37190 * @extends Roo.bootstrap.Component
37191 * Base class for layout managers.
37193 Roo.bootstrap.layout.Manager = function(config)
37195 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37201 /** false to disable window resize monitoring @type Boolean */
37202 this.monitorWindowResize = true;
37207 * Fires when a layout is performed.
37208 * @param {Roo.LayoutManager} this
37212 * @event regionresized
37213 * Fires when the user resizes a region.
37214 * @param {Roo.LayoutRegion} region The resized region
37215 * @param {Number} newSize The new size (width for east/west, height for north/south)
37217 "regionresized" : true,
37219 * @event regioncollapsed
37220 * Fires when a region is collapsed.
37221 * @param {Roo.LayoutRegion} region The collapsed region
37223 "regioncollapsed" : true,
37225 * @event regionexpanded
37226 * Fires when a region is expanded.
37227 * @param {Roo.LayoutRegion} region The expanded region
37229 "regionexpanded" : true
37231 this.updating = false;
37234 this.el = Roo.get(config.el);
37240 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37245 monitorWindowResize : true,
37251 onRender : function(ct, position)
37254 this.el = Roo.get(ct);
37257 //this.fireEvent('render',this);
37261 initEvents: function()
37265 // ie scrollbar fix
37266 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37267 document.body.scroll = "no";
37268 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37269 this.el.position('relative');
37271 this.id = this.el.id;
37272 this.el.addClass("roo-layout-container");
37273 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37274 if(this.el.dom != document.body ) {
37275 this.el.on('resize', this.layout,this);
37276 this.el.on('show', this.layout,this);
37282 * Returns true if this layout is currently being updated
37283 * @return {Boolean}
37285 isUpdating : function(){
37286 return this.updating;
37290 * Suspend the LayoutManager from doing auto-layouts while
37291 * making multiple add or remove calls
37293 beginUpdate : function(){
37294 this.updating = true;
37298 * Restore auto-layouts and optionally disable the manager from performing a layout
37299 * @param {Boolean} noLayout true to disable a layout update
37301 endUpdate : function(noLayout){
37302 this.updating = false;
37308 layout: function(){
37312 onRegionResized : function(region, newSize){
37313 this.fireEvent("regionresized", region, newSize);
37317 onRegionCollapsed : function(region){
37318 this.fireEvent("regioncollapsed", region);
37321 onRegionExpanded : function(region){
37322 this.fireEvent("regionexpanded", region);
37326 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37327 * performs box-model adjustments.
37328 * @return {Object} The size as an object {width: (the width), height: (the height)}
37330 getViewSize : function()
37333 if(this.el.dom != document.body){
37334 size = this.el.getSize();
37336 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37338 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37339 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37344 * Returns the Element this layout is bound to.
37345 * @return {Roo.Element}
37347 getEl : function(){
37352 * Returns the specified region.
37353 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37354 * @return {Roo.LayoutRegion}
37356 getRegion : function(target){
37357 return this.regions[target.toLowerCase()];
37360 onWindowResize : function(){
37361 if(this.monitorWindowResize){
37368 * Ext JS Library 1.1.1
37369 * Copyright(c) 2006-2007, Ext JS, LLC.
37371 * Originally Released Under LGPL - original licence link has changed is not relivant.
37374 * <script type="text/javascript">
37377 * @class Roo.bootstrap.layout.Border
37378 * @extends Roo.bootstrap.layout.Manager
37379 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37380 * please see: examples/bootstrap/nested.html<br><br>
37382 <b>The container the layout is rendered into can be either the body element or any other element.
37383 If it is not the body element, the container needs to either be an absolute positioned element,
37384 or you will need to add "position:relative" to the css of the container. You will also need to specify
37385 the container size if it is not the body element.</b>
37388 * Create a new Border
37389 * @param {Object} config Configuration options
37391 Roo.bootstrap.layout.Border = function(config){
37392 config = config || {};
37393 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37397 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37398 if(config[region]){
37399 config[region].region = region;
37400 this.addRegion(config[region]);
37406 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37408 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37410 parent : false, // this might point to a 'nest' or a ???
37413 * Creates and adds a new region if it doesn't already exist.
37414 * @param {String} target The target region key (north, south, east, west or center).
37415 * @param {Object} config The regions config object
37416 * @return {BorderLayoutRegion} The new region
37418 addRegion : function(config)
37420 if(!this.regions[config.region]){
37421 var r = this.factory(config);
37422 this.bindRegion(r);
37424 return this.regions[config.region];
37428 bindRegion : function(r){
37429 this.regions[r.config.region] = r;
37431 r.on("visibilitychange", this.layout, this);
37432 r.on("paneladded", this.layout, this);
37433 r.on("panelremoved", this.layout, this);
37434 r.on("invalidated", this.layout, this);
37435 r.on("resized", this.onRegionResized, this);
37436 r.on("collapsed", this.onRegionCollapsed, this);
37437 r.on("expanded", this.onRegionExpanded, this);
37441 * Performs a layout update.
37443 layout : function()
37445 if(this.updating) {
37449 // render all the rebions if they have not been done alreayd?
37450 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37451 if(this.regions[region] && !this.regions[region].bodyEl){
37452 this.regions[region].onRender(this.el)
37456 var size = this.getViewSize();
37457 var w = size.width;
37458 var h = size.height;
37463 //var x = 0, y = 0;
37465 var rs = this.regions;
37466 var north = rs["north"];
37467 var south = rs["south"];
37468 var west = rs["west"];
37469 var east = rs["east"];
37470 var center = rs["center"];
37471 //if(this.hideOnLayout){ // not supported anymore
37472 //c.el.setStyle("display", "none");
37474 if(north && north.isVisible()){
37475 var b = north.getBox();
37476 var m = north.getMargins();
37477 b.width = w - (m.left+m.right);
37480 centerY = b.height + b.y + m.bottom;
37481 centerH -= centerY;
37482 north.updateBox(this.safeBox(b));
37484 if(south && south.isVisible()){
37485 var b = south.getBox();
37486 var m = south.getMargins();
37487 b.width = w - (m.left+m.right);
37489 var totalHeight = (b.height + m.top + m.bottom);
37490 b.y = h - totalHeight + m.top;
37491 centerH -= totalHeight;
37492 south.updateBox(this.safeBox(b));
37494 if(west && west.isVisible()){
37495 var b = west.getBox();
37496 var m = west.getMargins();
37497 b.height = centerH - (m.top+m.bottom);
37499 b.y = centerY + m.top;
37500 var totalWidth = (b.width + m.left + m.right);
37501 centerX += totalWidth;
37502 centerW -= totalWidth;
37503 west.updateBox(this.safeBox(b));
37505 if(east && east.isVisible()){
37506 var b = east.getBox();
37507 var m = east.getMargins();
37508 b.height = centerH - (m.top+m.bottom);
37509 var totalWidth = (b.width + m.left + m.right);
37510 b.x = w - totalWidth + m.left;
37511 b.y = centerY + m.top;
37512 centerW -= totalWidth;
37513 east.updateBox(this.safeBox(b));
37516 var m = center.getMargins();
37518 x: centerX + m.left,
37519 y: centerY + m.top,
37520 width: centerW - (m.left+m.right),
37521 height: centerH - (m.top+m.bottom)
37523 //if(this.hideOnLayout){
37524 //center.el.setStyle("display", "block");
37526 center.updateBox(this.safeBox(centerBox));
37529 this.fireEvent("layout", this);
37533 safeBox : function(box){
37534 box.width = Math.max(0, box.width);
37535 box.height = Math.max(0, box.height);
37540 * Adds a ContentPanel (or subclass) to this layout.
37541 * @param {String} target The target region key (north, south, east, west or center).
37542 * @param {Roo.ContentPanel} panel The panel to add
37543 * @return {Roo.ContentPanel} The added panel
37545 add : function(target, panel){
37547 target = target.toLowerCase();
37548 return this.regions[target].add(panel);
37552 * Remove a ContentPanel (or subclass) to this layout.
37553 * @param {String} target The target region key (north, south, east, west or center).
37554 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37555 * @return {Roo.ContentPanel} The removed panel
37557 remove : function(target, panel){
37558 target = target.toLowerCase();
37559 return this.regions[target].remove(panel);
37563 * Searches all regions for a panel with the specified id
37564 * @param {String} panelId
37565 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37567 findPanel : function(panelId){
37568 var rs = this.regions;
37569 for(var target in rs){
37570 if(typeof rs[target] != "function"){
37571 var p = rs[target].getPanel(panelId);
37581 * Searches all regions for a panel with the specified id and activates (shows) it.
37582 * @param {String/ContentPanel} panelId The panels id or the panel itself
37583 * @return {Roo.ContentPanel} The shown panel or null
37585 showPanel : function(panelId) {
37586 var rs = this.regions;
37587 for(var target in rs){
37588 var r = rs[target];
37589 if(typeof r != "function"){
37590 if(r.hasPanel(panelId)){
37591 return r.showPanel(panelId);
37599 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37600 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37603 restoreState : function(provider){
37605 provider = Roo.state.Manager;
37607 var sm = new Roo.LayoutStateManager();
37608 sm.init(this, provider);
37614 * Adds a xtype elements to the layout.
37618 xtype : 'ContentPanel',
37625 xtype : 'NestedLayoutPanel',
37631 items : [ ... list of content panels or nested layout panels.. ]
37635 * @param {Object} cfg Xtype definition of item to add.
37637 addxtype : function(cfg)
37639 // basically accepts a pannel...
37640 // can accept a layout region..!?!?
37641 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37644 // theory? children can only be panels??
37646 //if (!cfg.xtype.match(/Panel$/)) {
37651 if (typeof(cfg.region) == 'undefined') {
37652 Roo.log("Failed to add Panel, region was not set");
37656 var region = cfg.region;
37662 xitems = cfg.items;
37667 if ( region == 'center') {
37668 Roo.log("Center: " + cfg.title);
37674 case 'Content': // ContentPanel (el, cfg)
37675 case 'Scroll': // ContentPanel (el, cfg)
37677 cfg.autoCreate = cfg.autoCreate || true;
37678 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37680 // var el = this.el.createChild();
37681 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37684 this.add(region, ret);
37688 case 'TreePanel': // our new panel!
37689 cfg.el = this.el.createChild();
37690 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37691 this.add(region, ret);
37696 // create a new Layout (which is a Border Layout...
37698 var clayout = cfg.layout;
37699 clayout.el = this.el.createChild();
37700 clayout.items = clayout.items || [];
37704 // replace this exitems with the clayout ones..
37705 xitems = clayout.items;
37707 // force background off if it's in center...
37708 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37709 cfg.background = false;
37711 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37714 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37715 //console.log('adding nested layout panel ' + cfg.toSource());
37716 this.add(region, ret);
37717 nb = {}; /// find first...
37722 // needs grid and region
37724 //var el = this.getRegion(region).el.createChild();
37726 *var el = this.el.createChild();
37727 // create the grid first...
37728 cfg.grid.container = el;
37729 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37732 if (region == 'center' && this.active ) {
37733 cfg.background = false;
37736 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37738 this.add(region, ret);
37740 if (cfg.background) {
37741 // render grid on panel activation (if panel background)
37742 ret.on('activate', function(gp) {
37743 if (!gp.grid.rendered) {
37744 // gp.grid.render(el);
37748 // cfg.grid.render(el);
37754 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37755 // it was the old xcomponent building that caused this before.
37756 // espeically if border is the top element in the tree.
37766 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37768 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37769 this.add(region, ret);
37773 throw "Can not add '" + cfg.xtype + "' to Border";
37779 this.beginUpdate();
37783 Roo.each(xitems, function(i) {
37784 region = nb && i.region ? i.region : false;
37786 var add = ret.addxtype(i);
37789 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37790 if (!i.background) {
37791 abn[region] = nb[region] ;
37798 // make the last non-background panel active..
37799 //if (nb) { Roo.log(abn); }
37802 for(var r in abn) {
37803 region = this.getRegion(r);
37805 // tried using nb[r], but it does not work..
37807 region.showPanel(abn[r]);
37818 factory : function(cfg)
37821 var validRegions = Roo.bootstrap.layout.Border.regions;
37823 var target = cfg.region;
37826 var r = Roo.bootstrap.layout;
37830 return new r.North(cfg);
37832 return new r.South(cfg);
37834 return new r.East(cfg);
37836 return new r.West(cfg);
37838 return new r.Center(cfg);
37840 throw 'Layout region "'+target+'" not supported.';
37847 * Ext JS Library 1.1.1
37848 * Copyright(c) 2006-2007, Ext JS, LLC.
37850 * Originally Released Under LGPL - original licence link has changed is not relivant.
37853 * <script type="text/javascript">
37857 * @class Roo.bootstrap.layout.Basic
37858 * @extends Roo.util.Observable
37859 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37860 * and does not have a titlebar, tabs or any other features. All it does is size and position
37861 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37862 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37863 * @cfg {string} region the region that it inhabits..
37864 * @cfg {bool} skipConfig skip config?
37868 Roo.bootstrap.layout.Basic = function(config){
37870 this.mgr = config.mgr;
37872 this.position = config.region;
37874 var skipConfig = config.skipConfig;
37878 * @scope Roo.BasicLayoutRegion
37882 * @event beforeremove
37883 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37884 * @param {Roo.LayoutRegion} this
37885 * @param {Roo.ContentPanel} panel The panel
37886 * @param {Object} e The cancel event object
37888 "beforeremove" : true,
37890 * @event invalidated
37891 * Fires when the layout for this region is changed.
37892 * @param {Roo.LayoutRegion} this
37894 "invalidated" : true,
37896 * @event visibilitychange
37897 * Fires when this region is shown or hidden
37898 * @param {Roo.LayoutRegion} this
37899 * @param {Boolean} visibility true or false
37901 "visibilitychange" : true,
37903 * @event paneladded
37904 * Fires when a panel is added.
37905 * @param {Roo.LayoutRegion} this
37906 * @param {Roo.ContentPanel} panel The panel
37908 "paneladded" : true,
37910 * @event panelremoved
37911 * Fires when a panel is removed.
37912 * @param {Roo.LayoutRegion} this
37913 * @param {Roo.ContentPanel} panel The panel
37915 "panelremoved" : true,
37917 * @event beforecollapse
37918 * Fires when this region before collapse.
37919 * @param {Roo.LayoutRegion} this
37921 "beforecollapse" : true,
37924 * Fires when this region is collapsed.
37925 * @param {Roo.LayoutRegion} this
37927 "collapsed" : true,
37930 * Fires when this region is expanded.
37931 * @param {Roo.LayoutRegion} this
37936 * Fires when this region is slid into view.
37937 * @param {Roo.LayoutRegion} this
37939 "slideshow" : true,
37942 * Fires when this region slides out of view.
37943 * @param {Roo.LayoutRegion} this
37945 "slidehide" : true,
37947 * @event panelactivated
37948 * Fires when a panel is activated.
37949 * @param {Roo.LayoutRegion} this
37950 * @param {Roo.ContentPanel} panel The activated panel
37952 "panelactivated" : true,
37955 * Fires when the user resizes this region.
37956 * @param {Roo.LayoutRegion} this
37957 * @param {Number} newSize The new size (width for east/west, height for north/south)
37961 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37962 this.panels = new Roo.util.MixedCollection();
37963 this.panels.getKey = this.getPanelId.createDelegate(this);
37965 this.activePanel = null;
37966 // ensure listeners are added...
37968 if (config.listeners || config.events) {
37969 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37970 listeners : config.listeners || {},
37971 events : config.events || {}
37975 if(skipConfig !== true){
37976 this.applyConfig(config);
37980 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37982 getPanelId : function(p){
37986 applyConfig : function(config){
37987 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37988 this.config = config;
37993 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37994 * the width, for horizontal (north, south) the height.
37995 * @param {Number} newSize The new width or height
37997 resizeTo : function(newSize){
37998 var el = this.el ? this.el :
37999 (this.activePanel ? this.activePanel.getEl() : null);
38001 switch(this.position){
38004 el.setWidth(newSize);
38005 this.fireEvent("resized", this, newSize);
38009 el.setHeight(newSize);
38010 this.fireEvent("resized", this, newSize);
38016 getBox : function(){
38017 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38020 getMargins : function(){
38021 return this.margins;
38024 updateBox : function(box){
38026 var el = this.activePanel.getEl();
38027 el.dom.style.left = box.x + "px";
38028 el.dom.style.top = box.y + "px";
38029 this.activePanel.setSize(box.width, box.height);
38033 * Returns the container element for this region.
38034 * @return {Roo.Element}
38036 getEl : function(){
38037 return this.activePanel;
38041 * Returns true if this region is currently visible.
38042 * @return {Boolean}
38044 isVisible : function(){
38045 return this.activePanel ? true : false;
38048 setActivePanel : function(panel){
38049 panel = this.getPanel(panel);
38050 if(this.activePanel && this.activePanel != panel){
38051 this.activePanel.setActiveState(false);
38052 this.activePanel.getEl().setLeftTop(-10000,-10000);
38054 this.activePanel = panel;
38055 panel.setActiveState(true);
38057 panel.setSize(this.box.width, this.box.height);
38059 this.fireEvent("panelactivated", this, panel);
38060 this.fireEvent("invalidated");
38064 * Show the specified panel.
38065 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38066 * @return {Roo.ContentPanel} The shown panel or null
38068 showPanel : function(panel){
38069 panel = this.getPanel(panel);
38071 this.setActivePanel(panel);
38077 * Get the active panel for this region.
38078 * @return {Roo.ContentPanel} The active panel or null
38080 getActivePanel : function(){
38081 return this.activePanel;
38085 * Add the passed ContentPanel(s)
38086 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38087 * @return {Roo.ContentPanel} The panel added (if only one was added)
38089 add : function(panel){
38090 if(arguments.length > 1){
38091 for(var i = 0, len = arguments.length; i < len; i++) {
38092 this.add(arguments[i]);
38096 if(this.hasPanel(panel)){
38097 this.showPanel(panel);
38100 var el = panel.getEl();
38101 if(el.dom.parentNode != this.mgr.el.dom){
38102 this.mgr.el.dom.appendChild(el.dom);
38104 if(panel.setRegion){
38105 panel.setRegion(this);
38107 this.panels.add(panel);
38108 el.setStyle("position", "absolute");
38109 if(!panel.background){
38110 this.setActivePanel(panel);
38111 if(this.config.initialSize && this.panels.getCount()==1){
38112 this.resizeTo(this.config.initialSize);
38115 this.fireEvent("paneladded", this, panel);
38120 * Returns true if the panel is in this region.
38121 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38122 * @return {Boolean}
38124 hasPanel : function(panel){
38125 if(typeof panel == "object"){ // must be panel obj
38126 panel = panel.getId();
38128 return this.getPanel(panel) ? true : false;
38132 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38133 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38134 * @param {Boolean} preservePanel Overrides the config preservePanel option
38135 * @return {Roo.ContentPanel} The panel that was removed
38137 remove : function(panel, preservePanel){
38138 panel = this.getPanel(panel);
38143 this.fireEvent("beforeremove", this, panel, e);
38144 if(e.cancel === true){
38147 var panelId = panel.getId();
38148 this.panels.removeKey(panelId);
38153 * Returns the panel specified or null if it's not in this region.
38154 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38155 * @return {Roo.ContentPanel}
38157 getPanel : function(id){
38158 if(typeof id == "object"){ // must be panel obj
38161 return this.panels.get(id);
38165 * Returns this regions position (north/south/east/west/center).
38168 getPosition: function(){
38169 return this.position;
38173 * Ext JS Library 1.1.1
38174 * Copyright(c) 2006-2007, Ext JS, LLC.
38176 * Originally Released Under LGPL - original licence link has changed is not relivant.
38179 * <script type="text/javascript">
38183 * @class Roo.bootstrap.layout.Region
38184 * @extends Roo.bootstrap.layout.Basic
38185 * This class represents a region in a layout manager.
38187 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38188 * @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})
38189 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38190 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38191 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38192 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38193 * @cfg {String} title The title for the region (overrides panel titles)
38194 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38195 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38196 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38197 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38198 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38199 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38200 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38201 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38202 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38203 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38205 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38206 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38207 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38208 * @cfg {Number} width For East/West panels
38209 * @cfg {Number} height For North/South panels
38210 * @cfg {Boolean} split To show the splitter
38211 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38213 * @cfg {string} cls Extra CSS classes to add to region
38215 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38216 * @cfg {string} region the region that it inhabits..
38219 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38220 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38222 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38223 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38224 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38226 Roo.bootstrap.layout.Region = function(config)
38228 this.applyConfig(config);
38230 var mgr = config.mgr;
38231 var pos = config.region;
38232 config.skipConfig = true;
38233 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38236 this.onRender(mgr.el);
38239 this.visible = true;
38240 this.collapsed = false;
38241 this.unrendered_panels = [];
38244 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38246 position: '', // set by wrapper (eg. north/south etc..)
38247 unrendered_panels : null, // unrendered panels.
38249 tabPosition : false,
38251 mgr: false, // points to 'Border'
38254 createBody : function(){
38255 /** This region's body element
38256 * @type Roo.Element */
38257 this.bodyEl = this.el.createChild({
38259 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38263 onRender: function(ctr, pos)
38265 var dh = Roo.DomHelper;
38266 /** This region's container element
38267 * @type Roo.Element */
38268 this.el = dh.append(ctr.dom, {
38270 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38272 /** This region's title element
38273 * @type Roo.Element */
38275 this.titleEl = dh.append(this.el.dom, {
38277 unselectable: "on",
38278 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38280 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38281 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38285 this.titleEl.enableDisplayMode();
38286 /** This region's title text element
38287 * @type HTMLElement */
38288 this.titleTextEl = this.titleEl.dom.firstChild;
38289 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38291 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38292 this.closeBtn.enableDisplayMode();
38293 this.closeBtn.on("click", this.closeClicked, this);
38294 this.closeBtn.hide();
38296 this.createBody(this.config);
38297 if(this.config.hideWhenEmpty){
38299 this.on("paneladded", this.validateVisibility, this);
38300 this.on("panelremoved", this.validateVisibility, this);
38302 if(this.autoScroll){
38303 this.bodyEl.setStyle("overflow", "auto");
38305 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38307 //if(c.titlebar !== false){
38308 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38309 this.titleEl.hide();
38311 this.titleEl.show();
38312 if(this.config.title){
38313 this.titleTextEl.innerHTML = this.config.title;
38317 if(this.config.collapsed){
38318 this.collapse(true);
38320 if(this.config.hidden){
38324 if (this.unrendered_panels && this.unrendered_panels.length) {
38325 for (var i =0;i< this.unrendered_panels.length; i++) {
38326 this.add(this.unrendered_panels[i]);
38328 this.unrendered_panels = null;
38334 applyConfig : function(c)
38337 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38338 var dh = Roo.DomHelper;
38339 if(c.titlebar !== false){
38340 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38341 this.collapseBtn.on("click", this.collapse, this);
38342 this.collapseBtn.enableDisplayMode();
38344 if(c.showPin === true || this.showPin){
38345 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38346 this.stickBtn.enableDisplayMode();
38347 this.stickBtn.on("click", this.expand, this);
38348 this.stickBtn.hide();
38353 /** This region's collapsed element
38354 * @type Roo.Element */
38357 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38358 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38361 if(c.floatable !== false){
38362 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38363 this.collapsedEl.on("click", this.collapseClick, this);
38366 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38367 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38368 id: "message", unselectable: "on", style:{"float":"left"}});
38369 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38371 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38372 this.expandBtn.on("click", this.expand, this);
38376 if(this.collapseBtn){
38377 this.collapseBtn.setVisible(c.collapsible == true);
38380 this.cmargins = c.cmargins || this.cmargins ||
38381 (this.position == "west" || this.position == "east" ?
38382 {top: 0, left: 2, right:2, bottom: 0} :
38383 {top: 2, left: 0, right:0, bottom: 2});
38385 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38388 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38390 this.autoScroll = c.autoScroll || false;
38395 this.duration = c.duration || .30;
38396 this.slideDuration = c.slideDuration || .45;
38401 * Returns true if this region is currently visible.
38402 * @return {Boolean}
38404 isVisible : function(){
38405 return this.visible;
38409 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38410 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38412 //setCollapsedTitle : function(title){
38413 // title = title || " ";
38414 // if(this.collapsedTitleTextEl){
38415 // this.collapsedTitleTextEl.innerHTML = title;
38419 getBox : function(){
38421 // if(!this.collapsed){
38422 b = this.el.getBox(false, true);
38424 // b = this.collapsedEl.getBox(false, true);
38429 getMargins : function(){
38430 return this.margins;
38431 //return this.collapsed ? this.cmargins : this.margins;
38434 highlight : function(){
38435 this.el.addClass("x-layout-panel-dragover");
38438 unhighlight : function(){
38439 this.el.removeClass("x-layout-panel-dragover");
38442 updateBox : function(box)
38444 if (!this.bodyEl) {
38445 return; // not rendered yet..
38449 if(!this.collapsed){
38450 this.el.dom.style.left = box.x + "px";
38451 this.el.dom.style.top = box.y + "px";
38452 this.updateBody(box.width, box.height);
38454 this.collapsedEl.dom.style.left = box.x + "px";
38455 this.collapsedEl.dom.style.top = box.y + "px";
38456 this.collapsedEl.setSize(box.width, box.height);
38459 this.tabs.autoSizeTabs();
38463 updateBody : function(w, h)
38466 this.el.setWidth(w);
38467 w -= this.el.getBorderWidth("rl");
38468 if(this.config.adjustments){
38469 w += this.config.adjustments[0];
38472 if(h !== null && h > 0){
38473 this.el.setHeight(h);
38474 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38475 h -= this.el.getBorderWidth("tb");
38476 if(this.config.adjustments){
38477 h += this.config.adjustments[1];
38479 this.bodyEl.setHeight(h);
38481 h = this.tabs.syncHeight(h);
38484 if(this.panelSize){
38485 w = w !== null ? w : this.panelSize.width;
38486 h = h !== null ? h : this.panelSize.height;
38488 if(this.activePanel){
38489 var el = this.activePanel.getEl();
38490 w = w !== null ? w : el.getWidth();
38491 h = h !== null ? h : el.getHeight();
38492 this.panelSize = {width: w, height: h};
38493 this.activePanel.setSize(w, h);
38495 if(Roo.isIE && this.tabs){
38496 this.tabs.el.repaint();
38501 * Returns the container element for this region.
38502 * @return {Roo.Element}
38504 getEl : function(){
38509 * Hides this region.
38512 //if(!this.collapsed){
38513 this.el.dom.style.left = "-2000px";
38516 // this.collapsedEl.dom.style.left = "-2000px";
38517 // this.collapsedEl.hide();
38519 this.visible = false;
38520 this.fireEvent("visibilitychange", this, false);
38524 * Shows this region if it was previously hidden.
38527 //if(!this.collapsed){
38530 // this.collapsedEl.show();
38532 this.visible = true;
38533 this.fireEvent("visibilitychange", this, true);
38536 closeClicked : function(){
38537 if(this.activePanel){
38538 this.remove(this.activePanel);
38542 collapseClick : function(e){
38544 e.stopPropagation();
38547 e.stopPropagation();
38553 * Collapses this region.
38554 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38557 collapse : function(skipAnim, skipCheck = false){
38558 if(this.collapsed) {
38562 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38564 this.collapsed = true;
38566 this.split.el.hide();
38568 if(this.config.animate && skipAnim !== true){
38569 this.fireEvent("invalidated", this);
38570 this.animateCollapse();
38572 this.el.setLocation(-20000,-20000);
38574 this.collapsedEl.show();
38575 this.fireEvent("collapsed", this);
38576 this.fireEvent("invalidated", this);
38582 animateCollapse : function(){
38587 * Expands this region if it was previously collapsed.
38588 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38589 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38592 expand : function(e, skipAnim){
38594 e.stopPropagation();
38596 if(!this.collapsed || this.el.hasActiveFx()) {
38600 this.afterSlideIn();
38603 this.collapsed = false;
38604 if(this.config.animate && skipAnim !== true){
38605 this.animateExpand();
38609 this.split.el.show();
38611 this.collapsedEl.setLocation(-2000,-2000);
38612 this.collapsedEl.hide();
38613 this.fireEvent("invalidated", this);
38614 this.fireEvent("expanded", this);
38618 animateExpand : function(){
38622 initTabs : function()
38624 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38626 var ts = new Roo.bootstrap.panel.Tabs({
38627 el: this.bodyEl.dom,
38629 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38630 disableTooltips: this.config.disableTabTips,
38631 toolbar : this.config.toolbar
38634 if(this.config.hideTabs){
38635 ts.stripWrap.setDisplayed(false);
38638 ts.resizeTabs = this.config.resizeTabs === true;
38639 ts.minTabWidth = this.config.minTabWidth || 40;
38640 ts.maxTabWidth = this.config.maxTabWidth || 250;
38641 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38642 ts.monitorResize = false;
38643 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38644 ts.bodyEl.addClass('roo-layout-tabs-body');
38645 this.panels.each(this.initPanelAsTab, this);
38648 initPanelAsTab : function(panel){
38649 var ti = this.tabs.addTab(
38653 this.config.closeOnTab && panel.isClosable(),
38656 if(panel.tabTip !== undefined){
38657 ti.setTooltip(panel.tabTip);
38659 ti.on("activate", function(){
38660 this.setActivePanel(panel);
38663 if(this.config.closeOnTab){
38664 ti.on("beforeclose", function(t, e){
38666 this.remove(panel);
38670 panel.tabItem = ti;
38675 updatePanelTitle : function(panel, title)
38677 if(this.activePanel == panel){
38678 this.updateTitle(title);
38681 var ti = this.tabs.getTab(panel.getEl().id);
38683 if(panel.tabTip !== undefined){
38684 ti.setTooltip(panel.tabTip);
38689 updateTitle : function(title){
38690 if(this.titleTextEl && !this.config.title){
38691 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38695 setActivePanel : function(panel)
38697 panel = this.getPanel(panel);
38698 if(this.activePanel && this.activePanel != panel){
38699 if(this.activePanel.setActiveState(false) === false){
38703 this.activePanel = panel;
38704 panel.setActiveState(true);
38705 if(this.panelSize){
38706 panel.setSize(this.panelSize.width, this.panelSize.height);
38709 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38711 this.updateTitle(panel.getTitle());
38713 this.fireEvent("invalidated", this);
38715 this.fireEvent("panelactivated", this, panel);
38719 * Shows the specified panel.
38720 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38721 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38723 showPanel : function(panel)
38725 panel = this.getPanel(panel);
38728 var tab = this.tabs.getTab(panel.getEl().id);
38729 if(tab.isHidden()){
38730 this.tabs.unhideTab(tab.id);
38734 this.setActivePanel(panel);
38741 * Get the active panel for this region.
38742 * @return {Roo.ContentPanel} The active panel or null
38744 getActivePanel : function(){
38745 return this.activePanel;
38748 validateVisibility : function(){
38749 if(this.panels.getCount() < 1){
38750 this.updateTitle(" ");
38751 this.closeBtn.hide();
38754 if(!this.isVisible()){
38761 * Adds the passed ContentPanel(s) to this region.
38762 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38763 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38765 add : function(panel)
38767 if(arguments.length > 1){
38768 for(var i = 0, len = arguments.length; i < len; i++) {
38769 this.add(arguments[i]);
38774 // if we have not been rendered yet, then we can not really do much of this..
38775 if (!this.bodyEl) {
38776 this.unrendered_panels.push(panel);
38783 if(this.hasPanel(panel)){
38784 this.showPanel(panel);
38787 panel.setRegion(this);
38788 this.panels.add(panel);
38789 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38790 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38791 // and hide them... ???
38792 this.bodyEl.dom.appendChild(panel.getEl().dom);
38793 if(panel.background !== true){
38794 this.setActivePanel(panel);
38796 this.fireEvent("paneladded", this, panel);
38803 this.initPanelAsTab(panel);
38807 if(panel.background !== true){
38808 this.tabs.activate(panel.getEl().id);
38810 this.fireEvent("paneladded", this, panel);
38815 * Hides the tab for the specified panel.
38816 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38818 hidePanel : function(panel){
38819 if(this.tabs && (panel = this.getPanel(panel))){
38820 this.tabs.hideTab(panel.getEl().id);
38825 * Unhides the tab for a previously hidden panel.
38826 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38828 unhidePanel : function(panel){
38829 if(this.tabs && (panel = this.getPanel(panel))){
38830 this.tabs.unhideTab(panel.getEl().id);
38834 clearPanels : function(){
38835 while(this.panels.getCount() > 0){
38836 this.remove(this.panels.first());
38841 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38842 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38843 * @param {Boolean} preservePanel Overrides the config preservePanel option
38844 * @return {Roo.ContentPanel} The panel that was removed
38846 remove : function(panel, preservePanel)
38848 panel = this.getPanel(panel);
38853 this.fireEvent("beforeremove", this, panel, e);
38854 if(e.cancel === true){
38857 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38858 var panelId = panel.getId();
38859 this.panels.removeKey(panelId);
38861 document.body.appendChild(panel.getEl().dom);
38864 this.tabs.removeTab(panel.getEl().id);
38865 }else if (!preservePanel){
38866 this.bodyEl.dom.removeChild(panel.getEl().dom);
38868 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38869 var p = this.panels.first();
38870 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38871 tempEl.appendChild(p.getEl().dom);
38872 this.bodyEl.update("");
38873 this.bodyEl.dom.appendChild(p.getEl().dom);
38875 this.updateTitle(p.getTitle());
38877 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38878 this.setActivePanel(p);
38880 panel.setRegion(null);
38881 if(this.activePanel == panel){
38882 this.activePanel = null;
38884 if(this.config.autoDestroy !== false && preservePanel !== true){
38885 try{panel.destroy();}catch(e){}
38887 this.fireEvent("panelremoved", this, panel);
38892 * Returns the TabPanel component used by this region
38893 * @return {Roo.TabPanel}
38895 getTabs : function(){
38899 createTool : function(parentEl, className){
38900 var btn = Roo.DomHelper.append(parentEl, {
38902 cls: "x-layout-tools-button",
38905 cls: "roo-layout-tools-button-inner " + className,
38909 btn.addClassOnOver("roo-layout-tools-button-over");
38914 * Ext JS Library 1.1.1
38915 * Copyright(c) 2006-2007, Ext JS, LLC.
38917 * Originally Released Under LGPL - original licence link has changed is not relivant.
38920 * <script type="text/javascript">
38926 * @class Roo.SplitLayoutRegion
38927 * @extends Roo.LayoutRegion
38928 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38930 Roo.bootstrap.layout.Split = function(config){
38931 this.cursor = config.cursor;
38932 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38935 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38937 splitTip : "Drag to resize.",
38938 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38939 useSplitTips : false,
38941 applyConfig : function(config){
38942 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38945 onRender : function(ctr,pos) {
38947 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38948 if(!this.config.split){
38953 var splitEl = Roo.DomHelper.append(ctr.dom, {
38955 id: this.el.id + "-split",
38956 cls: "roo-layout-split roo-layout-split-"+this.position,
38959 /** The SplitBar for this region
38960 * @type Roo.SplitBar */
38961 // does not exist yet...
38962 Roo.log([this.position, this.orientation]);
38964 this.split = new Roo.bootstrap.SplitBar({
38965 dragElement : splitEl,
38966 resizingElement: this.el,
38967 orientation : this.orientation
38970 this.split.on("moved", this.onSplitMove, this);
38971 this.split.useShim = this.config.useShim === true;
38972 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38973 if(this.useSplitTips){
38974 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38976 //if(config.collapsible){
38977 // this.split.el.on("dblclick", this.collapse, this);
38980 if(typeof this.config.minSize != "undefined"){
38981 this.split.minSize = this.config.minSize;
38983 if(typeof this.config.maxSize != "undefined"){
38984 this.split.maxSize = this.config.maxSize;
38986 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38987 this.hideSplitter();
38992 getHMaxSize : function(){
38993 var cmax = this.config.maxSize || 10000;
38994 var center = this.mgr.getRegion("center");
38995 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38998 getVMaxSize : function(){
38999 var cmax = this.config.maxSize || 10000;
39000 var center = this.mgr.getRegion("center");
39001 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39004 onSplitMove : function(split, newSize){
39005 this.fireEvent("resized", this, newSize);
39009 * Returns the {@link Roo.SplitBar} for this region.
39010 * @return {Roo.SplitBar}
39012 getSplitBar : function(){
39017 this.hideSplitter();
39018 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39021 hideSplitter : function(){
39023 this.split.el.setLocation(-2000,-2000);
39024 this.split.el.hide();
39030 this.split.el.show();
39032 Roo.bootstrap.layout.Split.superclass.show.call(this);
39035 beforeSlide: function(){
39036 if(Roo.isGecko){// firefox overflow auto bug workaround
39037 this.bodyEl.clip();
39039 this.tabs.bodyEl.clip();
39041 if(this.activePanel){
39042 this.activePanel.getEl().clip();
39044 if(this.activePanel.beforeSlide){
39045 this.activePanel.beforeSlide();
39051 afterSlide : function(){
39052 if(Roo.isGecko){// firefox overflow auto bug workaround
39053 this.bodyEl.unclip();
39055 this.tabs.bodyEl.unclip();
39057 if(this.activePanel){
39058 this.activePanel.getEl().unclip();
39059 if(this.activePanel.afterSlide){
39060 this.activePanel.afterSlide();
39066 initAutoHide : function(){
39067 if(this.autoHide !== false){
39068 if(!this.autoHideHd){
39069 var st = new Roo.util.DelayedTask(this.slideIn, this);
39070 this.autoHideHd = {
39071 "mouseout": function(e){
39072 if(!e.within(this.el, true)){
39076 "mouseover" : function(e){
39082 this.el.on(this.autoHideHd);
39086 clearAutoHide : function(){
39087 if(this.autoHide !== false){
39088 this.el.un("mouseout", this.autoHideHd.mouseout);
39089 this.el.un("mouseover", this.autoHideHd.mouseover);
39093 clearMonitor : function(){
39094 Roo.get(document).un("click", this.slideInIf, this);
39097 // these names are backwards but not changed for compat
39098 slideOut : function(){
39099 if(this.isSlid || this.el.hasActiveFx()){
39102 this.isSlid = true;
39103 if(this.collapseBtn){
39104 this.collapseBtn.hide();
39106 this.closeBtnState = this.closeBtn.getStyle('display');
39107 this.closeBtn.hide();
39109 this.stickBtn.show();
39112 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39113 this.beforeSlide();
39114 this.el.setStyle("z-index", 10001);
39115 this.el.slideIn(this.getSlideAnchor(), {
39116 callback: function(){
39118 this.initAutoHide();
39119 Roo.get(document).on("click", this.slideInIf, this);
39120 this.fireEvent("slideshow", this);
39127 afterSlideIn : function(){
39128 this.clearAutoHide();
39129 this.isSlid = false;
39130 this.clearMonitor();
39131 this.el.setStyle("z-index", "");
39132 if(this.collapseBtn){
39133 this.collapseBtn.show();
39135 this.closeBtn.setStyle('display', this.closeBtnState);
39137 this.stickBtn.hide();
39139 this.fireEvent("slidehide", this);
39142 slideIn : function(cb){
39143 if(!this.isSlid || this.el.hasActiveFx()){
39147 this.isSlid = false;
39148 this.beforeSlide();
39149 this.el.slideOut(this.getSlideAnchor(), {
39150 callback: function(){
39151 this.el.setLeftTop(-10000, -10000);
39153 this.afterSlideIn();
39161 slideInIf : function(e){
39162 if(!e.within(this.el)){
39167 animateCollapse : function(){
39168 this.beforeSlide();
39169 this.el.setStyle("z-index", 20000);
39170 var anchor = this.getSlideAnchor();
39171 this.el.slideOut(anchor, {
39172 callback : function(){
39173 this.el.setStyle("z-index", "");
39174 this.collapsedEl.slideIn(anchor, {duration:.3});
39176 this.el.setLocation(-10000,-10000);
39178 this.fireEvent("collapsed", this);
39185 animateExpand : function(){
39186 this.beforeSlide();
39187 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39188 this.el.setStyle("z-index", 20000);
39189 this.collapsedEl.hide({
39192 this.el.slideIn(this.getSlideAnchor(), {
39193 callback : function(){
39194 this.el.setStyle("z-index", "");
39197 this.split.el.show();
39199 this.fireEvent("invalidated", this);
39200 this.fireEvent("expanded", this);
39228 getAnchor : function(){
39229 return this.anchors[this.position];
39232 getCollapseAnchor : function(){
39233 return this.canchors[this.position];
39236 getSlideAnchor : function(){
39237 return this.sanchors[this.position];
39240 getAlignAdj : function(){
39241 var cm = this.cmargins;
39242 switch(this.position){
39258 getExpandAdj : function(){
39259 var c = this.collapsedEl, cm = this.cmargins;
39260 switch(this.position){
39262 return [-(cm.right+c.getWidth()+cm.left), 0];
39265 return [cm.right+c.getWidth()+cm.left, 0];
39268 return [0, -(cm.top+cm.bottom+c.getHeight())];
39271 return [0, cm.top+cm.bottom+c.getHeight()];
39277 * Ext JS Library 1.1.1
39278 * Copyright(c) 2006-2007, Ext JS, LLC.
39280 * Originally Released Under LGPL - original licence link has changed is not relivant.
39283 * <script type="text/javascript">
39286 * These classes are private internal classes
39288 Roo.bootstrap.layout.Center = function(config){
39289 config.region = "center";
39290 Roo.bootstrap.layout.Region.call(this, config);
39291 this.visible = true;
39292 this.minWidth = config.minWidth || 20;
39293 this.minHeight = config.minHeight || 20;
39296 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39298 // center panel can't be hidden
39302 // center panel can't be hidden
39305 getMinWidth: function(){
39306 return this.minWidth;
39309 getMinHeight: function(){
39310 return this.minHeight;
39324 Roo.bootstrap.layout.North = function(config)
39326 config.region = 'north';
39327 config.cursor = 'n-resize';
39329 Roo.bootstrap.layout.Split.call(this, config);
39333 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39334 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39335 this.split.el.addClass("roo-layout-split-v");
39337 //var size = config.initialSize || config.height;
39338 //if(this.el && typeof size != "undefined"){
39339 // this.el.setHeight(size);
39342 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39344 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39347 onRender : function(ctr, pos)
39349 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39350 var size = this.config.initialSize || this.config.height;
39351 if(this.el && typeof size != "undefined"){
39352 this.el.setHeight(size);
39357 getBox : function(){
39358 if(this.collapsed){
39359 return this.collapsedEl.getBox();
39361 var box = this.el.getBox();
39363 box.height += this.split.el.getHeight();
39368 updateBox : function(box){
39369 if(this.split && !this.collapsed){
39370 box.height -= this.split.el.getHeight();
39371 this.split.el.setLeft(box.x);
39372 this.split.el.setTop(box.y+box.height);
39373 this.split.el.setWidth(box.width);
39375 if(this.collapsed){
39376 this.updateBody(box.width, null);
39378 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39386 Roo.bootstrap.layout.South = function(config){
39387 config.region = 'south';
39388 config.cursor = 's-resize';
39389 Roo.bootstrap.layout.Split.call(this, config);
39391 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39392 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39393 this.split.el.addClass("roo-layout-split-v");
39398 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39399 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39401 onRender : function(ctr, pos)
39403 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39404 var size = this.config.initialSize || this.config.height;
39405 if(this.el && typeof size != "undefined"){
39406 this.el.setHeight(size);
39411 getBox : function(){
39412 if(this.collapsed){
39413 return this.collapsedEl.getBox();
39415 var box = this.el.getBox();
39417 var sh = this.split.el.getHeight();
39424 updateBox : function(box){
39425 if(this.split && !this.collapsed){
39426 var sh = this.split.el.getHeight();
39429 this.split.el.setLeft(box.x);
39430 this.split.el.setTop(box.y-sh);
39431 this.split.el.setWidth(box.width);
39433 if(this.collapsed){
39434 this.updateBody(box.width, null);
39436 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39440 Roo.bootstrap.layout.East = function(config){
39441 config.region = "east";
39442 config.cursor = "e-resize";
39443 Roo.bootstrap.layout.Split.call(this, config);
39445 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39446 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39447 this.split.el.addClass("roo-layout-split-h");
39451 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39452 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39454 onRender : function(ctr, pos)
39456 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39457 var size = this.config.initialSize || this.config.width;
39458 if(this.el && typeof size != "undefined"){
39459 this.el.setWidth(size);
39464 getBox : function(){
39465 if(this.collapsed){
39466 return this.collapsedEl.getBox();
39468 var box = this.el.getBox();
39470 var sw = this.split.el.getWidth();
39477 updateBox : function(box){
39478 if(this.split && !this.collapsed){
39479 var sw = this.split.el.getWidth();
39481 this.split.el.setLeft(box.x);
39482 this.split.el.setTop(box.y);
39483 this.split.el.setHeight(box.height);
39486 if(this.collapsed){
39487 this.updateBody(null, box.height);
39489 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39493 Roo.bootstrap.layout.West = function(config){
39494 config.region = "west";
39495 config.cursor = "w-resize";
39497 Roo.bootstrap.layout.Split.call(this, config);
39499 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39500 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39501 this.split.el.addClass("roo-layout-split-h");
39505 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39506 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39508 onRender: function(ctr, pos)
39510 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39511 var size = this.config.initialSize || this.config.width;
39512 if(typeof size != "undefined"){
39513 this.el.setWidth(size);
39517 getBox : function(){
39518 if(this.collapsed){
39519 return this.collapsedEl.getBox();
39521 var box = this.el.getBox();
39522 if (box.width == 0) {
39523 box.width = this.config.width; // kludge?
39526 box.width += this.split.el.getWidth();
39531 updateBox : function(box){
39532 if(this.split && !this.collapsed){
39533 var sw = this.split.el.getWidth();
39535 this.split.el.setLeft(box.x+box.width);
39536 this.split.el.setTop(box.y);
39537 this.split.el.setHeight(box.height);
39539 if(this.collapsed){
39540 this.updateBody(null, box.height);
39542 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39544 });Roo.namespace("Roo.bootstrap.panel");/*
39546 * Ext JS Library 1.1.1
39547 * Copyright(c) 2006-2007, Ext JS, LLC.
39549 * Originally Released Under LGPL - original licence link has changed is not relivant.
39552 * <script type="text/javascript">
39555 * @class Roo.ContentPanel
39556 * @extends Roo.util.Observable
39557 * A basic ContentPanel element.
39558 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39559 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39560 * @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
39561 * @cfg {Boolean} closable True if the panel can be closed/removed
39562 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39563 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39564 * @cfg {Toolbar} toolbar A toolbar for this panel
39565 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39566 * @cfg {String} title The title for this panel
39567 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39568 * @cfg {String} url Calls {@link #setUrl} with this value
39569 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39570 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39571 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39572 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39573 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39574 * @cfg {Boolean} badges render the badges
39575 * @cfg {String} cls extra classes to use
39576 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39579 * Create a new ContentPanel.
39580 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39581 * @param {String/Object} config A string to set only the title or a config object
39582 * @param {String} content (optional) Set the HTML content for this panel
39583 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39585 Roo.bootstrap.panel.Content = function( config){
39587 this.tpl = config.tpl || false;
39589 var el = config.el;
39590 var content = config.content;
39592 if(config.autoCreate){ // xtype is available if this is called from factory
39595 this.el = Roo.get(el);
39596 if(!this.el && config && config.autoCreate){
39597 if(typeof config.autoCreate == "object"){
39598 if(!config.autoCreate.id){
39599 config.autoCreate.id = config.id||el;
39601 this.el = Roo.DomHelper.append(document.body,
39602 config.autoCreate, true);
39606 cls: (config.cls || '') +
39607 (config.background ? ' bg-' + config.background : '') +
39608 " roo-layout-inactive-content",
39611 if (config.iframe) {
39615 style : 'border: 0px',
39616 src : 'about:blank'
39622 elcfg.html = config.html;
39626 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39627 if (config.iframe) {
39628 this.iframeEl = this.el.select('iframe',true).first();
39633 this.closable = false;
39634 this.loaded = false;
39635 this.active = false;
39638 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39640 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39642 this.wrapEl = this.el; //this.el.wrap();
39644 if (config.toolbar.items) {
39645 ti = config.toolbar.items ;
39646 delete config.toolbar.items ;
39650 this.toolbar.render(this.wrapEl, 'before');
39651 for(var i =0;i < ti.length;i++) {
39652 // Roo.log(['add child', items[i]]);
39653 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39655 this.toolbar.items = nitems;
39656 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39657 delete config.toolbar;
39661 // xtype created footer. - not sure if will work as we normally have to render first..
39662 if (this.footer && !this.footer.el && this.footer.xtype) {
39663 if (!this.wrapEl) {
39664 this.wrapEl = this.el.wrap();
39667 this.footer.container = this.wrapEl.createChild();
39669 this.footer = Roo.factory(this.footer, Roo);
39674 if(typeof config == "string"){
39675 this.title = config;
39677 Roo.apply(this, config);
39681 this.resizeEl = Roo.get(this.resizeEl, true);
39683 this.resizeEl = this.el;
39685 // handle view.xtype
39693 * Fires when this panel is activated.
39694 * @param {Roo.ContentPanel} this
39698 * @event deactivate
39699 * Fires when this panel is activated.
39700 * @param {Roo.ContentPanel} this
39702 "deactivate" : true,
39706 * Fires when this panel is resized if fitToFrame is true.
39707 * @param {Roo.ContentPanel} this
39708 * @param {Number} width The width after any component adjustments
39709 * @param {Number} height The height after any component adjustments
39715 * Fires when this tab is created
39716 * @param {Roo.ContentPanel} this
39727 if(this.autoScroll && !this.iframe){
39728 this.resizeEl.setStyle("overflow", "auto");
39730 // fix randome scrolling
39731 //this.el.on('scroll', function() {
39732 // Roo.log('fix random scolling');
39733 // this.scrollTo('top',0);
39736 content = content || this.content;
39738 this.setContent(content);
39740 if(config && config.url){
39741 this.setUrl(this.url, this.params, this.loadOnce);
39746 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39748 if (this.view && typeof(this.view.xtype) != 'undefined') {
39749 this.view.el = this.el.appendChild(document.createElement("div"));
39750 this.view = Roo.factory(this.view);
39751 this.view.render && this.view.render(false, '');
39755 this.fireEvent('render', this);
39758 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39768 setRegion : function(region){
39769 this.region = region;
39770 this.setActiveClass(region && !this.background);
39774 setActiveClass: function(state)
39777 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39778 this.el.setStyle('position','relative');
39780 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39781 this.el.setStyle('position', 'absolute');
39786 * Returns the toolbar for this Panel if one was configured.
39787 * @return {Roo.Toolbar}
39789 getToolbar : function(){
39790 return this.toolbar;
39793 setActiveState : function(active)
39795 this.active = active;
39796 this.setActiveClass(active);
39798 if(this.fireEvent("deactivate", this) === false){
39803 this.fireEvent("activate", this);
39807 * Updates this panel's element (not for iframe)
39808 * @param {String} content The new content
39809 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39811 setContent : function(content, loadScripts){
39816 this.el.update(content, loadScripts);
39819 ignoreResize : function(w, h){
39820 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39823 this.lastSize = {width: w, height: h};
39828 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39829 * @return {Roo.UpdateManager} The UpdateManager
39831 getUpdateManager : function(){
39835 return this.el.getUpdateManager();
39838 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39839 * Does not work with IFRAME contents
39840 * @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:
39843 url: "your-url.php",
39844 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39845 callback: yourFunction,
39846 scope: yourObject, //(optional scope)
39849 text: "Loading...",
39855 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39856 * 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.
39857 * @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}
39858 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39859 * @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.
39860 * @return {Roo.ContentPanel} this
39868 var um = this.el.getUpdateManager();
39869 um.update.apply(um, arguments);
39875 * 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.
39876 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39877 * @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)
39878 * @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)
39879 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39881 setUrl : function(url, params, loadOnce){
39883 this.iframeEl.dom.src = url;
39887 if(this.refreshDelegate){
39888 this.removeListener("activate", this.refreshDelegate);
39890 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39891 this.on("activate", this.refreshDelegate);
39892 return this.el.getUpdateManager();
39895 _handleRefresh : function(url, params, loadOnce){
39896 if(!loadOnce || !this.loaded){
39897 var updater = this.el.getUpdateManager();
39898 updater.update(url, params, this._setLoaded.createDelegate(this));
39902 _setLoaded : function(){
39903 this.loaded = true;
39907 * Returns this panel's id
39910 getId : function(){
39915 * Returns this panel's element - used by regiosn to add.
39916 * @return {Roo.Element}
39918 getEl : function(){
39919 return this.wrapEl || this.el;
39924 adjustForComponents : function(width, height)
39926 //Roo.log('adjustForComponents ');
39927 if(this.resizeEl != this.el){
39928 width -= this.el.getFrameWidth('lr');
39929 height -= this.el.getFrameWidth('tb');
39932 var te = this.toolbar.getEl();
39933 te.setWidth(width);
39934 height -= te.getHeight();
39937 var te = this.footer.getEl();
39938 te.setWidth(width);
39939 height -= te.getHeight();
39943 if(this.adjustments){
39944 width += this.adjustments[0];
39945 height += this.adjustments[1];
39947 return {"width": width, "height": height};
39950 setSize : function(width, height){
39951 if(this.fitToFrame && !this.ignoreResize(width, height)){
39952 if(this.fitContainer && this.resizeEl != this.el){
39953 this.el.setSize(width, height);
39955 var size = this.adjustForComponents(width, height);
39957 this.iframeEl.setSize(width,height);
39960 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39961 this.fireEvent('resize', this, size.width, size.height);
39968 * Returns this panel's title
39971 getTitle : function(){
39973 if (typeof(this.title) != 'object') {
39978 for (var k in this.title) {
39979 if (!this.title.hasOwnProperty(k)) {
39983 if (k.indexOf('-') >= 0) {
39984 var s = k.split('-');
39985 for (var i = 0; i<s.length; i++) {
39986 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39989 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39996 * Set this panel's title
39997 * @param {String} title
39999 setTitle : function(title){
40000 this.title = title;
40002 this.region.updatePanelTitle(this, title);
40007 * Returns true is this panel was configured to be closable
40008 * @return {Boolean}
40010 isClosable : function(){
40011 return this.closable;
40014 beforeSlide : function(){
40016 this.resizeEl.clip();
40019 afterSlide : function(){
40021 this.resizeEl.unclip();
40025 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40026 * Will fail silently if the {@link #setUrl} method has not been called.
40027 * This does not activate the panel, just updates its content.
40029 refresh : function(){
40030 if(this.refreshDelegate){
40031 this.loaded = false;
40032 this.refreshDelegate();
40037 * Destroys this panel
40039 destroy : function(){
40040 this.el.removeAllListeners();
40041 var tempEl = document.createElement("span");
40042 tempEl.appendChild(this.el.dom);
40043 tempEl.innerHTML = "";
40049 * form - if the content panel contains a form - this is a reference to it.
40050 * @type {Roo.form.Form}
40054 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40055 * This contains a reference to it.
40061 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40071 * @param {Object} cfg Xtype definition of item to add.
40075 getChildContainer: function () {
40076 return this.getEl();
40081 var ret = new Roo.factory(cfg);
40086 if (cfg.xtype.match(/^Form$/)) {
40089 //if (this.footer) {
40090 // el = this.footer.container.insertSibling(false, 'before');
40092 el = this.el.createChild();
40095 this.form = new Roo.form.Form(cfg);
40098 if ( this.form.allItems.length) {
40099 this.form.render(el.dom);
40103 // should only have one of theses..
40104 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40105 // views.. should not be just added - used named prop 'view''
40107 cfg.el = this.el.appendChild(document.createElement("div"));
40110 var ret = new Roo.factory(cfg);
40112 ret.render && ret.render(false, ''); // render blank..
40122 * @class Roo.bootstrap.panel.Grid
40123 * @extends Roo.bootstrap.panel.Content
40125 * Create a new GridPanel.
40126 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40127 * @param {Object} config A the config object
40133 Roo.bootstrap.panel.Grid = function(config)
40137 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40138 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40140 config.el = this.wrapper;
40141 //this.el = this.wrapper;
40143 if (config.container) {
40144 // ctor'ed from a Border/panel.grid
40147 this.wrapper.setStyle("overflow", "hidden");
40148 this.wrapper.addClass('roo-grid-container');
40153 if(config.toolbar){
40154 var tool_el = this.wrapper.createChild();
40155 this.toolbar = Roo.factory(config.toolbar);
40157 if (config.toolbar.items) {
40158 ti = config.toolbar.items ;
40159 delete config.toolbar.items ;
40163 this.toolbar.render(tool_el);
40164 for(var i =0;i < ti.length;i++) {
40165 // Roo.log(['add child', items[i]]);
40166 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40168 this.toolbar.items = nitems;
40170 delete config.toolbar;
40173 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40174 config.grid.scrollBody = true;;
40175 config.grid.monitorWindowResize = false; // turn off autosizing
40176 config.grid.autoHeight = false;
40177 config.grid.autoWidth = false;
40179 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40181 if (config.background) {
40182 // render grid on panel activation (if panel background)
40183 this.on('activate', function(gp) {
40184 if (!gp.grid.rendered) {
40185 gp.grid.render(this.wrapper);
40186 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40191 this.grid.render(this.wrapper);
40192 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40195 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40196 // ??? needed ??? config.el = this.wrapper;
40201 // xtype created footer. - not sure if will work as we normally have to render first..
40202 if (this.footer && !this.footer.el && this.footer.xtype) {
40204 var ctr = this.grid.getView().getFooterPanel(true);
40205 this.footer.dataSource = this.grid.dataSource;
40206 this.footer = Roo.factory(this.footer, Roo);
40207 this.footer.render(ctr);
40217 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40218 getId : function(){
40219 return this.grid.id;
40223 * Returns the grid for this panel
40224 * @return {Roo.bootstrap.Table}
40226 getGrid : function(){
40230 setSize : function(width, height){
40231 if(!this.ignoreResize(width, height)){
40232 var grid = this.grid;
40233 var size = this.adjustForComponents(width, height);
40234 // tfoot is not a footer?
40237 var gridel = grid.getGridEl();
40238 gridel.setSize(size.width, size.height);
40240 var tbd = grid.getGridEl().select('tbody', true).first();
40241 var thd = grid.getGridEl().select('thead',true).first();
40242 var tbf= grid.getGridEl().select('tfoot', true).first();
40245 size.height -= tbf.getHeight();
40248 size.height -= thd.getHeight();
40251 tbd.setSize(size.width, size.height );
40252 // this is for the account management tab -seems to work there.
40253 var thd = grid.getGridEl().select('thead',true).first();
40255 // tbd.setSize(size.width, size.height - thd.getHeight());
40264 beforeSlide : function(){
40265 this.grid.getView().scroller.clip();
40268 afterSlide : function(){
40269 this.grid.getView().scroller.unclip();
40272 destroy : function(){
40273 this.grid.destroy();
40275 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40280 * @class Roo.bootstrap.panel.Nest
40281 * @extends Roo.bootstrap.panel.Content
40283 * Create a new Panel, that can contain a layout.Border.
40286 * @param {Roo.BorderLayout} layout The layout for this panel
40287 * @param {String/Object} config A string to set only the title or a config object
40289 Roo.bootstrap.panel.Nest = function(config)
40291 // construct with only one argument..
40292 /* FIXME - implement nicer consturctors
40293 if (layout.layout) {
40295 layout = config.layout;
40296 delete config.layout;
40298 if (layout.xtype && !layout.getEl) {
40299 // then layout needs constructing..
40300 layout = Roo.factory(layout, Roo);
40304 config.el = config.layout.getEl();
40306 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40308 config.layout.monitorWindowResize = false; // turn off autosizing
40309 this.layout = config.layout;
40310 this.layout.getEl().addClass("roo-layout-nested-layout");
40311 this.layout.parent = this;
40318 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40320 setSize : function(width, height){
40321 if(!this.ignoreResize(width, height)){
40322 var size = this.adjustForComponents(width, height);
40323 var el = this.layout.getEl();
40324 if (size.height < 1) {
40325 el.setWidth(size.width);
40327 el.setSize(size.width, size.height);
40329 var touch = el.dom.offsetWidth;
40330 this.layout.layout();
40331 // ie requires a double layout on the first pass
40332 if(Roo.isIE && !this.initialized){
40333 this.initialized = true;
40334 this.layout.layout();
40339 // activate all subpanels if not currently active..
40341 setActiveState : function(active){
40342 this.active = active;
40343 this.setActiveClass(active);
40346 this.fireEvent("deactivate", this);
40350 this.fireEvent("activate", this);
40351 // not sure if this should happen before or after..
40352 if (!this.layout) {
40353 return; // should not happen..
40356 for (var r in this.layout.regions) {
40357 reg = this.layout.getRegion(r);
40358 if (reg.getActivePanel()) {
40359 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40360 reg.setActivePanel(reg.getActivePanel());
40363 if (!reg.panels.length) {
40366 reg.showPanel(reg.getPanel(0));
40375 * Returns the nested BorderLayout for this panel
40376 * @return {Roo.BorderLayout}
40378 getLayout : function(){
40379 return this.layout;
40383 * Adds a xtype elements to the layout of the nested panel
40387 xtype : 'ContentPanel',
40394 xtype : 'NestedLayoutPanel',
40400 items : [ ... list of content panels or nested layout panels.. ]
40404 * @param {Object} cfg Xtype definition of item to add.
40406 addxtype : function(cfg) {
40407 return this.layout.addxtype(cfg);
40412 * Ext JS Library 1.1.1
40413 * Copyright(c) 2006-2007, Ext JS, LLC.
40415 * Originally Released Under LGPL - original licence link has changed is not relivant.
40418 * <script type="text/javascript">
40421 * @class Roo.TabPanel
40422 * @extends Roo.util.Observable
40423 * A lightweight tab container.
40427 // basic tabs 1, built from existing content
40428 var tabs = new Roo.TabPanel("tabs1");
40429 tabs.addTab("script", "View Script");
40430 tabs.addTab("markup", "View Markup");
40431 tabs.activate("script");
40433 // more advanced tabs, built from javascript
40434 var jtabs = new Roo.TabPanel("jtabs");
40435 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40437 // set up the UpdateManager
40438 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40439 var updater = tab2.getUpdateManager();
40440 updater.setDefaultUrl("ajax1.htm");
40441 tab2.on('activate', updater.refresh, updater, true);
40443 // Use setUrl for Ajax loading
40444 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40445 tab3.setUrl("ajax2.htm", null, true);
40448 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40451 jtabs.activate("jtabs-1");
40454 * Create a new TabPanel.
40455 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40456 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40458 Roo.bootstrap.panel.Tabs = function(config){
40460 * The container element for this TabPanel.
40461 * @type Roo.Element
40463 this.el = Roo.get(config.el);
40466 if(typeof config == "boolean"){
40467 this.tabPosition = config ? "bottom" : "top";
40469 Roo.apply(this, config);
40473 if(this.tabPosition == "bottom"){
40474 // if tabs are at the bottom = create the body first.
40475 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40476 this.el.addClass("roo-tabs-bottom");
40478 // next create the tabs holders
40480 if (this.tabPosition == "west"){
40482 var reg = this.region; // fake it..
40484 if (!reg.mgr.parent) {
40487 reg = reg.mgr.parent.region;
40489 Roo.log("got nest?");
40491 if (reg.mgr.getRegion('west')) {
40492 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40493 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40494 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40495 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40496 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40504 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40505 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40506 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40507 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40512 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40515 // finally - if tabs are at the top, then create the body last..
40516 if(this.tabPosition != "bottom"){
40517 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40518 * @type Roo.Element
40520 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40521 this.el.addClass("roo-tabs-top");
40525 this.bodyEl.setStyle("position", "relative");
40527 this.active = null;
40528 this.activateDelegate = this.activate.createDelegate(this);
40533 * Fires when the active tab changes
40534 * @param {Roo.TabPanel} this
40535 * @param {Roo.TabPanelItem} activePanel The new active tab
40539 * @event beforetabchange
40540 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40541 * @param {Roo.TabPanel} this
40542 * @param {Object} e Set cancel to true on this object to cancel the tab change
40543 * @param {Roo.TabPanelItem} tab The tab being changed to
40545 "beforetabchange" : true
40548 Roo.EventManager.onWindowResize(this.onResize, this);
40549 this.cpad = this.el.getPadding("lr");
40550 this.hiddenCount = 0;
40553 // toolbar on the tabbar support...
40554 if (this.toolbar) {
40555 alert("no toolbar support yet");
40556 this.toolbar = false;
40558 var tcfg = this.toolbar;
40559 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40560 this.toolbar = new Roo.Toolbar(tcfg);
40561 if (Roo.isSafari) {
40562 var tbl = tcfg.container.child('table', true);
40563 tbl.setAttribute('width', '100%');
40571 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40574 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40576 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40578 tabPosition : "top",
40580 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40582 currentTabWidth : 0,
40584 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40588 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40592 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40594 preferredTabWidth : 175,
40596 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40598 resizeTabs : false,
40600 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40602 monitorResize : true,
40604 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40606 toolbar : false, // set by caller..
40608 region : false, /// set by caller
40610 disableTooltips : true, // not used yet...
40613 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40614 * @param {String} id The id of the div to use <b>or create</b>
40615 * @param {String} text The text for the tab
40616 * @param {String} content (optional) Content to put in the TabPanelItem body
40617 * @param {Boolean} closable (optional) True to create a close icon on the tab
40618 * @return {Roo.TabPanelItem} The created TabPanelItem
40620 addTab : function(id, text, content, closable, tpl)
40622 var item = new Roo.bootstrap.panel.TabItem({
40626 closable : closable,
40629 this.addTabItem(item);
40631 item.setContent(content);
40637 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40638 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40639 * @return {Roo.TabPanelItem}
40641 getTab : function(id){
40642 return this.items[id];
40646 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40647 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40649 hideTab : function(id){
40650 var t = this.items[id];
40653 this.hiddenCount++;
40654 this.autoSizeTabs();
40659 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40660 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40662 unhideTab : function(id){
40663 var t = this.items[id];
40665 t.setHidden(false);
40666 this.hiddenCount--;
40667 this.autoSizeTabs();
40672 * Adds an existing {@link Roo.TabPanelItem}.
40673 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40675 addTabItem : function(item)
40677 this.items[item.id] = item;
40678 this.items.push(item);
40679 this.autoSizeTabs();
40680 // if(this.resizeTabs){
40681 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40682 // this.autoSizeTabs();
40684 // item.autoSize();
40689 * Removes a {@link Roo.TabPanelItem}.
40690 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40692 removeTab : function(id){
40693 var items = this.items;
40694 var tab = items[id];
40695 if(!tab) { return; }
40696 var index = items.indexOf(tab);
40697 if(this.active == tab && items.length > 1){
40698 var newTab = this.getNextAvailable(index);
40703 this.stripEl.dom.removeChild(tab.pnode.dom);
40704 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40705 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40707 items.splice(index, 1);
40708 delete this.items[tab.id];
40709 tab.fireEvent("close", tab);
40710 tab.purgeListeners();
40711 this.autoSizeTabs();
40714 getNextAvailable : function(start){
40715 var items = this.items;
40717 // look for a next tab that will slide over to
40718 // replace the one being removed
40719 while(index < items.length){
40720 var item = items[++index];
40721 if(item && !item.isHidden()){
40725 // if one isn't found select the previous tab (on the left)
40728 var item = items[--index];
40729 if(item && !item.isHidden()){
40737 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40738 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40740 disableTab : function(id){
40741 var tab = this.items[id];
40742 if(tab && this.active != tab){
40748 * Enables a {@link Roo.TabPanelItem} that is disabled.
40749 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40751 enableTab : function(id){
40752 var tab = this.items[id];
40757 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40758 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40759 * @return {Roo.TabPanelItem} The TabPanelItem.
40761 activate : function(id)
40763 //Roo.log('activite:' + id);
40765 var tab = this.items[id];
40769 if(tab == this.active || tab.disabled){
40773 this.fireEvent("beforetabchange", this, e, tab);
40774 if(e.cancel !== true && !tab.disabled){
40776 this.active.hide();
40778 this.active = this.items[id];
40779 this.active.show();
40780 this.fireEvent("tabchange", this, this.active);
40786 * Gets the active {@link Roo.TabPanelItem}.
40787 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40789 getActiveTab : function(){
40790 return this.active;
40794 * Updates the tab body element to fit the height of the container element
40795 * for overflow scrolling
40796 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40798 syncHeight : function(targetHeight){
40799 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40800 var bm = this.bodyEl.getMargins();
40801 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40802 this.bodyEl.setHeight(newHeight);
40806 onResize : function(){
40807 if(this.monitorResize){
40808 this.autoSizeTabs();
40813 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40815 beginUpdate : function(){
40816 this.updating = true;
40820 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40822 endUpdate : function(){
40823 this.updating = false;
40824 this.autoSizeTabs();
40828 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40830 autoSizeTabs : function()
40832 var count = this.items.length;
40833 var vcount = count - this.hiddenCount;
40836 this.stripEl.hide();
40838 this.stripEl.show();
40841 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40846 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40847 var availWidth = Math.floor(w / vcount);
40848 var b = this.stripBody;
40849 if(b.getWidth() > w){
40850 var tabs = this.items;
40851 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40852 if(availWidth < this.minTabWidth){
40853 /*if(!this.sleft){ // incomplete scrolling code
40854 this.createScrollButtons();
40857 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40860 if(this.currentTabWidth < this.preferredTabWidth){
40861 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40867 * Returns the number of tabs in this TabPanel.
40870 getCount : function(){
40871 return this.items.length;
40875 * Resizes all the tabs to the passed width
40876 * @param {Number} The new width
40878 setTabWidth : function(width){
40879 this.currentTabWidth = width;
40880 for(var i = 0, len = this.items.length; i < len; i++) {
40881 if(!this.items[i].isHidden()) {
40882 this.items[i].setWidth(width);
40888 * Destroys this TabPanel
40889 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40891 destroy : function(removeEl){
40892 Roo.EventManager.removeResizeListener(this.onResize, this);
40893 for(var i = 0, len = this.items.length; i < len; i++){
40894 this.items[i].purgeListeners();
40896 if(removeEl === true){
40897 this.el.update("");
40902 createStrip : function(container)
40904 var strip = document.createElement("nav");
40905 strip.className = Roo.bootstrap.version == 4 ?
40906 "navbar-light bg-light" :
40907 "navbar navbar-default"; //"x-tabs-wrap";
40908 container.appendChild(strip);
40912 createStripList : function(strip)
40914 // div wrapper for retard IE
40915 // returns the "tr" element.
40916 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40917 //'<div class="x-tabs-strip-wrap">'+
40918 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40919 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40920 return strip.firstChild; //.firstChild.firstChild.firstChild;
40922 createBody : function(container)
40924 var body = document.createElement("div");
40925 Roo.id(body, "tab-body");
40926 //Roo.fly(body).addClass("x-tabs-body");
40927 Roo.fly(body).addClass("tab-content");
40928 container.appendChild(body);
40931 createItemBody :function(bodyEl, id){
40932 var body = Roo.getDom(id);
40934 body = document.createElement("div");
40937 //Roo.fly(body).addClass("x-tabs-item-body");
40938 Roo.fly(body).addClass("tab-pane");
40939 bodyEl.insertBefore(body, bodyEl.firstChild);
40943 createStripElements : function(stripEl, text, closable, tpl)
40945 var td = document.createElement("li"); // was td..
40946 td.className = 'nav-item';
40948 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40951 stripEl.appendChild(td);
40953 td.className = "x-tabs-closable";
40954 if(!this.closeTpl){
40955 this.closeTpl = new Roo.Template(
40956 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40957 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40958 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40961 var el = this.closeTpl.overwrite(td, {"text": text});
40962 var close = el.getElementsByTagName("div")[0];
40963 var inner = el.getElementsByTagName("em")[0];
40964 return {"el": el, "close": close, "inner": inner};
40967 // not sure what this is..
40968 // if(!this.tabTpl){
40969 //this.tabTpl = new Roo.Template(
40970 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40971 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40973 // this.tabTpl = new Roo.Template(
40974 // '<a href="#">' +
40975 // '<span unselectable="on"' +
40976 // (this.disableTooltips ? '' : ' title="{text}"') +
40977 // ' >{text}</span></a>'
40983 var template = tpl || this.tabTpl || false;
40986 template = new Roo.Template(
40987 Roo.bootstrap.version == 4 ?
40989 '<a class="nav-link" href="#" unselectable="on"' +
40990 (this.disableTooltips ? '' : ' title="{text}"') +
40993 '<a class="nav-link" href="#">' +
40994 '<span unselectable="on"' +
40995 (this.disableTooltips ? '' : ' title="{text}"') +
40996 ' >{text}</span></a>'
41001 switch (typeof(template)) {
41005 template = new Roo.Template(template);
41011 var el = template.overwrite(td, {"text": text});
41013 var inner = el.getElementsByTagName("span")[0];
41015 return {"el": el, "inner": inner};
41023 * @class Roo.TabPanelItem
41024 * @extends Roo.util.Observable
41025 * Represents an individual item (tab plus body) in a TabPanel.
41026 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41027 * @param {String} id The id of this TabPanelItem
41028 * @param {String} text The text for the tab of this TabPanelItem
41029 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41031 Roo.bootstrap.panel.TabItem = function(config){
41033 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41034 * @type Roo.TabPanel
41036 this.tabPanel = config.panel;
41038 * The id for this TabPanelItem
41041 this.id = config.id;
41043 this.disabled = false;
41045 this.text = config.text;
41047 this.loaded = false;
41048 this.closable = config.closable;
41051 * The body element for this TabPanelItem.
41052 * @type Roo.Element
41054 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41055 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41056 this.bodyEl.setStyle("display", "block");
41057 this.bodyEl.setStyle("zoom", "1");
41058 //this.hideAction();
41060 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41062 this.el = Roo.get(els.el);
41063 this.inner = Roo.get(els.inner, true);
41064 this.textEl = Roo.bootstrap.version == 4 ?
41065 this.el : Roo.get(this.el.dom.firstChild, true);
41067 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41068 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41071 // this.el.on("mousedown", this.onTabMouseDown, this);
41072 this.el.on("click", this.onTabClick, this);
41074 if(config.closable){
41075 var c = Roo.get(els.close, true);
41076 c.dom.title = this.closeText;
41077 c.addClassOnOver("close-over");
41078 c.on("click", this.closeClick, this);
41084 * Fires when this tab becomes the active tab.
41085 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41086 * @param {Roo.TabPanelItem} this
41090 * @event beforeclose
41091 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41092 * @param {Roo.TabPanelItem} this
41093 * @param {Object} e Set cancel to true on this object to cancel the close.
41095 "beforeclose": true,
41098 * Fires when this tab is closed.
41099 * @param {Roo.TabPanelItem} this
41103 * @event deactivate
41104 * Fires when this tab is no longer the active tab.
41105 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41106 * @param {Roo.TabPanelItem} this
41108 "deactivate" : true
41110 this.hidden = false;
41112 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41115 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41117 purgeListeners : function(){
41118 Roo.util.Observable.prototype.purgeListeners.call(this);
41119 this.el.removeAllListeners();
41122 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41125 this.status_node.addClass("active");
41128 this.tabPanel.stripWrap.repaint();
41130 this.fireEvent("activate", this.tabPanel, this);
41134 * Returns true if this tab is the active tab.
41135 * @return {Boolean}
41137 isActive : function(){
41138 return this.tabPanel.getActiveTab() == this;
41142 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41145 this.status_node.removeClass("active");
41147 this.fireEvent("deactivate", this.tabPanel, this);
41150 hideAction : function(){
41151 this.bodyEl.hide();
41152 this.bodyEl.setStyle("position", "absolute");
41153 this.bodyEl.setLeft("-20000px");
41154 this.bodyEl.setTop("-20000px");
41157 showAction : function(){
41158 this.bodyEl.setStyle("position", "relative");
41159 this.bodyEl.setTop("");
41160 this.bodyEl.setLeft("");
41161 this.bodyEl.show();
41165 * Set the tooltip for the tab.
41166 * @param {String} tooltip The tab's tooltip
41168 setTooltip : function(text){
41169 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41170 this.textEl.dom.qtip = text;
41171 this.textEl.dom.removeAttribute('title');
41173 this.textEl.dom.title = text;
41177 onTabClick : function(e){
41178 e.preventDefault();
41179 this.tabPanel.activate(this.id);
41182 onTabMouseDown : function(e){
41183 e.preventDefault();
41184 this.tabPanel.activate(this.id);
41187 getWidth : function(){
41188 return this.inner.getWidth();
41191 setWidth : function(width){
41192 var iwidth = width - this.linode.getPadding("lr");
41193 this.inner.setWidth(iwidth);
41194 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41195 this.linode.setWidth(width);
41199 * Show or hide the tab
41200 * @param {Boolean} hidden True to hide or false to show.
41202 setHidden : function(hidden){
41203 this.hidden = hidden;
41204 this.linode.setStyle("display", hidden ? "none" : "");
41208 * Returns true if this tab is "hidden"
41209 * @return {Boolean}
41211 isHidden : function(){
41212 return this.hidden;
41216 * Returns the text for this tab
41219 getText : function(){
41223 autoSize : function(){
41224 //this.el.beginMeasure();
41225 this.textEl.setWidth(1);
41227 * #2804 [new] Tabs in Roojs
41228 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41230 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41231 //this.el.endMeasure();
41235 * Sets the text for the tab (Note: this also sets the tooltip text)
41236 * @param {String} text The tab's text and tooltip
41238 setText : function(text){
41240 this.textEl.update(text);
41241 this.setTooltip(text);
41242 //if(!this.tabPanel.resizeTabs){
41243 // this.autoSize();
41247 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41249 activate : function(){
41250 this.tabPanel.activate(this.id);
41254 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41256 disable : function(){
41257 if(this.tabPanel.active != this){
41258 this.disabled = true;
41259 this.status_node.addClass("disabled");
41264 * Enables this TabPanelItem if it was previously disabled.
41266 enable : function(){
41267 this.disabled = false;
41268 this.status_node.removeClass("disabled");
41272 * Sets the content for this TabPanelItem.
41273 * @param {String} content The content
41274 * @param {Boolean} loadScripts true to look for and load scripts
41276 setContent : function(content, loadScripts){
41277 this.bodyEl.update(content, loadScripts);
41281 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41282 * @return {Roo.UpdateManager} The UpdateManager
41284 getUpdateManager : function(){
41285 return this.bodyEl.getUpdateManager();
41289 * Set a URL to be used to load the content for this TabPanelItem.
41290 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41291 * @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)
41292 * @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)
41293 * @return {Roo.UpdateManager} The UpdateManager
41295 setUrl : function(url, params, loadOnce){
41296 if(this.refreshDelegate){
41297 this.un('activate', this.refreshDelegate);
41299 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41300 this.on("activate", this.refreshDelegate);
41301 return this.bodyEl.getUpdateManager();
41305 _handleRefresh : function(url, params, loadOnce){
41306 if(!loadOnce || !this.loaded){
41307 var updater = this.bodyEl.getUpdateManager();
41308 updater.update(url, params, this._setLoaded.createDelegate(this));
41313 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41314 * Will fail silently if the setUrl method has not been called.
41315 * This does not activate the panel, just updates its content.
41317 refresh : function(){
41318 if(this.refreshDelegate){
41319 this.loaded = false;
41320 this.refreshDelegate();
41325 _setLoaded : function(){
41326 this.loaded = true;
41330 closeClick : function(e){
41333 this.fireEvent("beforeclose", this, o);
41334 if(o.cancel !== true){
41335 this.tabPanel.removeTab(this.id);
41339 * The text displayed in the tooltip for the close icon.
41342 closeText : "Close this tab"
41345 * This script refer to:
41346 * Title: International Telephone Input
41347 * Author: Jack O'Connor
41348 * Code version: v12.1.12
41349 * Availability: https://github.com/jackocnr/intl-tel-input.git
41352 Roo.bootstrap.PhoneInputData = function() {
41355 "Afghanistan (افغانستان)",
41360 "Albania (Shqipëri)",
41365 "Algeria (الجزائر)",
41390 "Antigua and Barbuda",
41400 "Armenia (Հայաստան)",
41416 "Austria (Österreich)",
41421 "Azerbaijan (Azərbaycan)",
41431 "Bahrain (البحرين)",
41436 "Bangladesh (বাংলাদেশ)",
41446 "Belarus (Беларусь)",
41451 "Belgium (België)",
41481 "Bosnia and Herzegovina (Босна и Херцеговина)",
41496 "British Indian Ocean Territory",
41501 "British Virgin Islands",
41511 "Bulgaria (България)",
41521 "Burundi (Uburundi)",
41526 "Cambodia (កម្ពុជា)",
41531 "Cameroon (Cameroun)",
41540 ["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"]
41543 "Cape Verde (Kabu Verdi)",
41548 "Caribbean Netherlands",
41559 "Central African Republic (République centrafricaine)",
41579 "Christmas Island",
41585 "Cocos (Keeling) Islands",
41596 "Comoros (جزر القمر)",
41601 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41606 "Congo (Republic) (Congo-Brazzaville)",
41626 "Croatia (Hrvatska)",
41647 "Czech Republic (Česká republika)",
41652 "Denmark (Danmark)",
41667 "Dominican Republic (República Dominicana)",
41671 ["809", "829", "849"]
41689 "Equatorial Guinea (Guinea Ecuatorial)",
41709 "Falkland Islands (Islas Malvinas)",
41714 "Faroe Islands (Føroyar)",
41735 "French Guiana (Guyane française)",
41740 "French Polynesia (Polynésie française)",
41755 "Georgia (საქართველო)",
41760 "Germany (Deutschland)",
41780 "Greenland (Kalaallit Nunaat)",
41817 "Guinea-Bissau (Guiné Bissau)",
41842 "Hungary (Magyarország)",
41847 "Iceland (Ísland)",
41867 "Iraq (العراق)",
41883 "Israel (ישראל)",
41910 "Jordan (الأردن)",
41915 "Kazakhstan (Казахстан)",
41936 "Kuwait (الكويت)",
41941 "Kyrgyzstan (Кыргызстан)",
41951 "Latvia (Latvija)",
41956 "Lebanon (لبنان)",
41971 "Libya (ليبيا)",
41981 "Lithuania (Lietuva)",
41996 "Macedonia (FYROM) (Македонија)",
42001 "Madagascar (Madagasikara)",
42031 "Marshall Islands",
42041 "Mauritania (موريتانيا)",
42046 "Mauritius (Moris)",
42067 "Moldova (Republica Moldova)",
42077 "Mongolia (Монгол)",
42082 "Montenegro (Crna Gora)",
42092 "Morocco (المغرب)",
42098 "Mozambique (Moçambique)",
42103 "Myanmar (Burma) (မြန်မာ)",
42108 "Namibia (Namibië)",
42123 "Netherlands (Nederland)",
42128 "New Caledonia (Nouvelle-Calédonie)",
42163 "North Korea (조선 민주주의 인민 공화국)",
42168 "Northern Mariana Islands",
42184 "Pakistan (پاکستان)",
42194 "Palestine (فلسطين)",
42204 "Papua New Guinea",
42246 "Réunion (La Réunion)",
42252 "Romania (România)",
42268 "Saint Barthélemy",
42279 "Saint Kitts and Nevis",
42289 "Saint Martin (Saint-Martin (partie française))",
42295 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42300 "Saint Vincent and the Grenadines",
42315 "São Tomé and Príncipe (São Tomé e Príncipe)",
42320 "Saudi Arabia (المملكة العربية السعودية)",
42325 "Senegal (Sénégal)",
42355 "Slovakia (Slovensko)",
42360 "Slovenia (Slovenija)",
42370 "Somalia (Soomaaliya)",
42380 "South Korea (대한민국)",
42385 "South Sudan (جنوب السودان)",
42395 "Sri Lanka (ශ්රී ලංකාව)",
42400 "Sudan (السودان)",
42410 "Svalbard and Jan Mayen",
42421 "Sweden (Sverige)",
42426 "Switzerland (Schweiz)",
42431 "Syria (سوريا)",
42476 "Trinidad and Tobago",
42481 "Tunisia (تونس)",
42486 "Turkey (Türkiye)",
42496 "Turks and Caicos Islands",
42506 "U.S. Virgin Islands",
42516 "Ukraine (Україна)",
42521 "United Arab Emirates (الإمارات العربية المتحدة)",
42543 "Uzbekistan (Oʻzbekiston)",
42553 "Vatican City (Città del Vaticano)",
42564 "Vietnam (Việt Nam)",
42569 "Wallis and Futuna (Wallis-et-Futuna)",
42574 "Western Sahara (الصحراء الغربية)",
42580 "Yemen (اليمن)",
42604 * This script refer to:
42605 * Title: International Telephone Input
42606 * Author: Jack O'Connor
42607 * Code version: v12.1.12
42608 * Availability: https://github.com/jackocnr/intl-tel-input.git
42612 * @class Roo.bootstrap.PhoneInput
42613 * @extends Roo.bootstrap.TriggerField
42614 * An input with International dial-code selection
42616 * @cfg {String} defaultDialCode default '+852'
42617 * @cfg {Array} preferedCountries default []
42620 * Create a new PhoneInput.
42621 * @param {Object} config Configuration options
42624 Roo.bootstrap.PhoneInput = function(config) {
42625 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42628 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42630 listWidth: undefined,
42632 selectedClass: 'active',
42634 invalidClass : "has-warning",
42636 validClass: 'has-success',
42638 allowed: '0123456789',
42643 * @cfg {String} defaultDialCode The default dial code when initializing the input
42645 defaultDialCode: '+852',
42648 * @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
42650 preferedCountries: false,
42652 getAutoCreate : function()
42654 var data = Roo.bootstrap.PhoneInputData();
42655 var align = this.labelAlign || this.parentLabelAlign();
42658 this.allCountries = [];
42659 this.dialCodeMapping = [];
42661 for (var i = 0; i < data.length; i++) {
42663 this.allCountries[i] = {
42667 priority: c[3] || 0,
42668 areaCodes: c[4] || null
42670 this.dialCodeMapping[c[2]] = {
42673 priority: c[3] || 0,
42674 areaCodes: c[4] || null
42686 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42687 maxlength: this.max_length,
42688 cls : 'form-control tel-input',
42689 autocomplete: 'new-password'
42692 var hiddenInput = {
42695 cls: 'hidden-tel-input'
42699 hiddenInput.name = this.name;
42702 if (this.disabled) {
42703 input.disabled = true;
42706 var flag_container = {
42723 cls: this.hasFeedback ? 'has-feedback' : '',
42729 cls: 'dial-code-holder',
42736 cls: 'roo-select2-container input-group',
42743 if (this.fieldLabel.length) {
42746 tooltip: 'This field is required'
42752 cls: 'control-label',
42758 html: this.fieldLabel
42761 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42767 if(this.indicatorpos == 'right') {
42768 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42775 if(align == 'left') {
42783 if(this.labelWidth > 12){
42784 label.style = "width: " + this.labelWidth + 'px';
42786 if(this.labelWidth < 13 && this.labelmd == 0){
42787 this.labelmd = this.labelWidth;
42789 if(this.labellg > 0){
42790 label.cls += ' col-lg-' + this.labellg;
42791 input.cls += ' col-lg-' + (12 - this.labellg);
42793 if(this.labelmd > 0){
42794 label.cls += ' col-md-' + this.labelmd;
42795 container.cls += ' col-md-' + (12 - this.labelmd);
42797 if(this.labelsm > 0){
42798 label.cls += ' col-sm-' + this.labelsm;
42799 container.cls += ' col-sm-' + (12 - this.labelsm);
42801 if(this.labelxs > 0){
42802 label.cls += ' col-xs-' + this.labelxs;
42803 container.cls += ' col-xs-' + (12 - this.labelxs);
42813 var settings = this;
42815 ['xs','sm','md','lg'].map(function(size){
42816 if (settings[size]) {
42817 cfg.cls += ' col-' + size + '-' + settings[size];
42821 this.store = new Roo.data.Store({
42822 proxy : new Roo.data.MemoryProxy({}),
42823 reader : new Roo.data.JsonReader({
42834 'name' : 'dialCode',
42838 'name' : 'priority',
42842 'name' : 'areaCodes',
42849 if(!this.preferedCountries) {
42850 this.preferedCountries = [
42857 var p = this.preferedCountries.reverse();
42860 for (var i = 0; i < p.length; i++) {
42861 for (var j = 0; j < this.allCountries.length; j++) {
42862 if(this.allCountries[j].iso2 == p[i]) {
42863 var t = this.allCountries[j];
42864 this.allCountries.splice(j,1);
42865 this.allCountries.unshift(t);
42871 this.store.proxy.data = {
42873 data: this.allCountries
42879 initEvents : function()
42882 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42884 this.indicator = this.indicatorEl();
42885 this.flag = this.flagEl();
42886 this.dialCodeHolder = this.dialCodeHolderEl();
42888 this.trigger = this.el.select('div.flag-box',true).first();
42889 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42894 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42895 _this.list.setWidth(lw);
42898 this.list.on('mouseover', this.onViewOver, this);
42899 this.list.on('mousemove', this.onViewMove, this);
42900 this.inputEl().on("keyup", this.onKeyUp, this);
42901 this.inputEl().on("keypress", this.onKeyPress, this);
42903 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42905 this.view = new Roo.View(this.list, this.tpl, {
42906 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42909 this.view.on('click', this.onViewClick, this);
42910 this.setValue(this.defaultDialCode);
42913 onTriggerClick : function(e)
42915 Roo.log('trigger click');
42920 if(this.isExpanded()){
42922 this.hasFocus = false;
42924 this.store.load({});
42925 this.hasFocus = true;
42930 isExpanded : function()
42932 return this.list.isVisible();
42935 collapse : function()
42937 if(!this.isExpanded()){
42941 Roo.get(document).un('mousedown', this.collapseIf, this);
42942 Roo.get(document).un('mousewheel', this.collapseIf, this);
42943 this.fireEvent('collapse', this);
42947 expand : function()
42951 if(this.isExpanded() || !this.hasFocus){
42955 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42956 this.list.setWidth(lw);
42959 this.restrictHeight();
42961 Roo.get(document).on('mousedown', this.collapseIf, this);
42962 Roo.get(document).on('mousewheel', this.collapseIf, this);
42964 this.fireEvent('expand', this);
42967 restrictHeight : function()
42969 this.list.alignTo(this.inputEl(), this.listAlign);
42970 this.list.alignTo(this.inputEl(), this.listAlign);
42973 onViewOver : function(e, t)
42975 if(this.inKeyMode){
42978 var item = this.view.findItemFromChild(t);
42981 var index = this.view.indexOf(item);
42982 this.select(index, false);
42987 onViewClick : function(view, doFocus, el, e)
42989 var index = this.view.getSelectedIndexes()[0];
42991 var r = this.store.getAt(index);
42994 this.onSelect(r, index);
42996 if(doFocus !== false && !this.blockFocus){
42997 this.inputEl().focus();
43001 onViewMove : function(e, t)
43003 this.inKeyMode = false;
43006 select : function(index, scrollIntoView)
43008 this.selectedIndex = index;
43009 this.view.select(index);
43010 if(scrollIntoView !== false){
43011 var el = this.view.getNode(index);
43013 this.list.scrollChildIntoView(el, false);
43018 createList : function()
43020 this.list = Roo.get(document.body).createChild({
43022 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43023 style: 'display:none'
43026 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43029 collapseIf : function(e)
43031 var in_combo = e.within(this.el);
43032 var in_list = e.within(this.list);
43033 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43035 if (in_combo || in_list || is_list) {
43041 onSelect : function(record, index)
43043 if(this.fireEvent('beforeselect', this, record, index) !== false){
43045 this.setFlagClass(record.data.iso2);
43046 this.setDialCode(record.data.dialCode);
43047 this.hasFocus = false;
43049 this.fireEvent('select', this, record, index);
43053 flagEl : function()
43055 var flag = this.el.select('div.flag',true).first();
43062 dialCodeHolderEl : function()
43064 var d = this.el.select('input.dial-code-holder',true).first();
43071 setDialCode : function(v)
43073 this.dialCodeHolder.dom.value = '+'+v;
43076 setFlagClass : function(n)
43078 this.flag.dom.className = 'flag '+n;
43081 getValue : function()
43083 var v = this.inputEl().getValue();
43084 if(this.dialCodeHolder) {
43085 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43090 setValue : function(v)
43092 var d = this.getDialCode(v);
43094 //invalid dial code
43095 if(v.length == 0 || !d || d.length == 0) {
43097 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43098 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43104 this.setFlagClass(this.dialCodeMapping[d].iso2);
43105 this.setDialCode(d);
43106 this.inputEl().dom.value = v.replace('+'+d,'');
43107 this.hiddenEl().dom.value = this.getValue();
43112 getDialCode : function(v)
43116 if (v.length == 0) {
43117 return this.dialCodeHolder.dom.value;
43121 if (v.charAt(0) != "+") {
43124 var numericChars = "";
43125 for (var i = 1; i < v.length; i++) {
43126 var c = v.charAt(i);
43129 if (this.dialCodeMapping[numericChars]) {
43130 dialCode = v.substr(1, i);
43132 if (numericChars.length == 4) {
43142 this.setValue(this.defaultDialCode);
43146 hiddenEl : function()
43148 return this.el.select('input.hidden-tel-input',true).first();
43151 // after setting val
43152 onKeyUp : function(e){
43153 this.setValue(this.getValue());
43156 onKeyPress : function(e){
43157 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43164 * @class Roo.bootstrap.MoneyField
43165 * @extends Roo.bootstrap.ComboBox
43166 * Bootstrap MoneyField class
43169 * Create a new MoneyField.
43170 * @param {Object} config Configuration options
43173 Roo.bootstrap.MoneyField = function(config) {
43175 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43179 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43182 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43184 allowDecimals : true,
43186 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43188 decimalSeparator : ".",
43190 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43192 decimalPrecision : 0,
43194 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43196 allowNegative : true,
43198 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43202 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43204 minValue : Number.NEGATIVE_INFINITY,
43206 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43208 maxValue : Number.MAX_VALUE,
43210 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43212 minText : "The minimum value for this field is {0}",
43214 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43216 maxText : "The maximum value for this field is {0}",
43218 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43219 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43221 nanText : "{0} is not a valid number",
43223 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43227 * @cfg {String} defaults currency of the MoneyField
43228 * value should be in lkey
43230 defaultCurrency : false,
43232 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43234 thousandsDelimiter : false,
43236 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43247 getAutoCreate : function()
43249 var align = this.labelAlign || this.parentLabelAlign();
43261 cls : 'form-control roo-money-amount-input',
43262 autocomplete: 'new-password'
43265 var hiddenInput = {
43269 cls: 'hidden-number-input'
43272 if(this.max_length) {
43273 input.maxlength = this.max_length;
43277 hiddenInput.name = this.name;
43280 if (this.disabled) {
43281 input.disabled = true;
43284 var clg = 12 - this.inputlg;
43285 var cmd = 12 - this.inputmd;
43286 var csm = 12 - this.inputsm;
43287 var cxs = 12 - this.inputxs;
43291 cls : 'row roo-money-field',
43295 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43299 cls: 'roo-select2-container input-group',
43303 cls : 'form-control roo-money-currency-input',
43304 autocomplete: 'new-password',
43306 name : this.currencyName
43310 cls : 'input-group-addon',
43324 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43328 cls: this.hasFeedback ? 'has-feedback' : '',
43339 if (this.fieldLabel.length) {
43342 tooltip: 'This field is required'
43348 cls: 'control-label',
43354 html: this.fieldLabel
43357 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43363 if(this.indicatorpos == 'right') {
43364 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43371 if(align == 'left') {
43379 if(this.labelWidth > 12){
43380 label.style = "width: " + this.labelWidth + 'px';
43382 if(this.labelWidth < 13 && this.labelmd == 0){
43383 this.labelmd = this.labelWidth;
43385 if(this.labellg > 0){
43386 label.cls += ' col-lg-' + this.labellg;
43387 input.cls += ' col-lg-' + (12 - this.labellg);
43389 if(this.labelmd > 0){
43390 label.cls += ' col-md-' + this.labelmd;
43391 container.cls += ' col-md-' + (12 - this.labelmd);
43393 if(this.labelsm > 0){
43394 label.cls += ' col-sm-' + this.labelsm;
43395 container.cls += ' col-sm-' + (12 - this.labelsm);
43397 if(this.labelxs > 0){
43398 label.cls += ' col-xs-' + this.labelxs;
43399 container.cls += ' col-xs-' + (12 - this.labelxs);
43410 var settings = this;
43412 ['xs','sm','md','lg'].map(function(size){
43413 if (settings[size]) {
43414 cfg.cls += ' col-' + size + '-' + settings[size];
43421 initEvents : function()
43423 this.indicator = this.indicatorEl();
43425 this.initCurrencyEvent();
43427 this.initNumberEvent();
43430 initCurrencyEvent : function()
43433 throw "can not find store for combo";
43436 this.store = Roo.factory(this.store, Roo.data);
43437 this.store.parent = this;
43441 this.triggerEl = this.el.select('.input-group-addon', true).first();
43443 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43448 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43449 _this.list.setWidth(lw);
43452 this.list.on('mouseover', this.onViewOver, this);
43453 this.list.on('mousemove', this.onViewMove, this);
43454 this.list.on('scroll', this.onViewScroll, this);
43457 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43460 this.view = new Roo.View(this.list, this.tpl, {
43461 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43464 this.view.on('click', this.onViewClick, this);
43466 this.store.on('beforeload', this.onBeforeLoad, this);
43467 this.store.on('load', this.onLoad, this);
43468 this.store.on('loadexception', this.onLoadException, this);
43470 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43471 "up" : function(e){
43472 this.inKeyMode = true;
43476 "down" : function(e){
43477 if(!this.isExpanded()){
43478 this.onTriggerClick();
43480 this.inKeyMode = true;
43485 "enter" : function(e){
43488 if(this.fireEvent("specialkey", this, e)){
43489 this.onViewClick(false);
43495 "esc" : function(e){
43499 "tab" : function(e){
43502 if(this.fireEvent("specialkey", this, e)){
43503 this.onViewClick(false);
43511 doRelay : function(foo, bar, hname){
43512 if(hname == 'down' || this.scope.isExpanded()){
43513 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43521 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43525 initNumberEvent : function(e)
43527 this.inputEl().on("keydown" , this.fireKey, this);
43528 this.inputEl().on("focus", this.onFocus, this);
43529 this.inputEl().on("blur", this.onBlur, this);
43531 this.inputEl().relayEvent('keyup', this);
43533 if(this.indicator){
43534 this.indicator.addClass('invisible');
43537 this.originalValue = this.getValue();
43539 if(this.validationEvent == 'keyup'){
43540 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43541 this.inputEl().on('keyup', this.filterValidation, this);
43543 else if(this.validationEvent !== false){
43544 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43547 if(this.selectOnFocus){
43548 this.on("focus", this.preFocus, this);
43551 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43552 this.inputEl().on("keypress", this.filterKeys, this);
43554 this.inputEl().relayEvent('keypress', this);
43557 var allowed = "0123456789";
43559 if(this.allowDecimals){
43560 allowed += this.decimalSeparator;
43563 if(this.allowNegative){
43567 if(this.thousandsDelimiter) {
43571 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43573 var keyPress = function(e){
43575 var k = e.getKey();
43577 var c = e.getCharCode();
43580 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43581 allowed.indexOf(String.fromCharCode(c)) === -1
43587 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43591 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43596 this.inputEl().on("keypress", keyPress, this);
43600 onTriggerClick : function(e)
43607 this.loadNext = false;
43609 if(this.isExpanded()){
43614 this.hasFocus = true;
43616 if(this.triggerAction == 'all') {
43617 this.doQuery(this.allQuery, true);
43621 this.doQuery(this.getRawValue());
43624 getCurrency : function()
43626 var v = this.currencyEl().getValue();
43631 restrictHeight : function()
43633 this.list.alignTo(this.currencyEl(), this.listAlign);
43634 this.list.alignTo(this.currencyEl(), this.listAlign);
43637 onViewClick : function(view, doFocus, el, e)
43639 var index = this.view.getSelectedIndexes()[0];
43641 var r = this.store.getAt(index);
43644 this.onSelect(r, index);
43648 onSelect : function(record, index){
43650 if(this.fireEvent('beforeselect', this, record, index) !== false){
43652 this.setFromCurrencyData(index > -1 ? record.data : false);
43656 this.fireEvent('select', this, record, index);
43660 setFromCurrencyData : function(o)
43664 this.lastCurrency = o;
43666 if (this.currencyField) {
43667 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43669 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43672 this.lastSelectionText = currency;
43674 //setting default currency
43675 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43676 this.setCurrency(this.defaultCurrency);
43680 this.setCurrency(currency);
43683 setFromData : function(o)
43687 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43689 this.setFromCurrencyData(c);
43694 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43696 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43699 this.setValue(value);
43703 setCurrency : function(v)
43705 this.currencyValue = v;
43708 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43713 setValue : function(v)
43715 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43721 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43723 this.inputEl().dom.value = (v == '') ? '' :
43724 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43726 if(!this.allowZero && v === '0') {
43727 this.hiddenEl().dom.value = '';
43728 this.inputEl().dom.value = '';
43735 getRawValue : function()
43737 var v = this.inputEl().getValue();
43742 getValue : function()
43744 return this.fixPrecision(this.parseValue(this.getRawValue()));
43747 parseValue : function(value)
43749 if(this.thousandsDelimiter) {
43751 r = new RegExp(",", "g");
43752 value = value.replace(r, "");
43755 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43756 return isNaN(value) ? '' : value;
43760 fixPrecision : function(value)
43762 if(this.thousandsDelimiter) {
43764 r = new RegExp(",", "g");
43765 value = value.replace(r, "");
43768 var nan = isNaN(value);
43770 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43771 return nan ? '' : value;
43773 return parseFloat(value).toFixed(this.decimalPrecision);
43776 decimalPrecisionFcn : function(v)
43778 return Math.floor(v);
43781 validateValue : function(value)
43783 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43787 var num = this.parseValue(value);
43790 this.markInvalid(String.format(this.nanText, value));
43794 if(num < this.minValue){
43795 this.markInvalid(String.format(this.minText, this.minValue));
43799 if(num > this.maxValue){
43800 this.markInvalid(String.format(this.maxText, this.maxValue));
43807 validate : function()
43809 if(this.disabled || this.allowBlank){
43814 var currency = this.getCurrency();
43816 if(this.validateValue(this.getRawValue()) && currency.length){
43821 this.markInvalid();
43825 getName: function()
43830 beforeBlur : function()
43836 var v = this.parseValue(this.getRawValue());
43843 onBlur : function()
43847 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43848 //this.el.removeClass(this.focusClass);
43851 this.hasFocus = false;
43853 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43857 var v = this.getValue();
43859 if(String(v) !== String(this.startValue)){
43860 this.fireEvent('change', this, v, this.startValue);
43863 this.fireEvent("blur", this);
43866 inputEl : function()
43868 return this.el.select('.roo-money-amount-input', true).first();
43871 currencyEl : function()
43873 return this.el.select('.roo-money-currency-input', true).first();
43876 hiddenEl : function()
43878 return this.el.select('input.hidden-number-input',true).first();
43882 * @class Roo.bootstrap.BezierSignature
43883 * @extends Roo.bootstrap.Component
43884 * Bootstrap BezierSignature class
43885 * This script refer to:
43886 * Title: Signature Pad
43888 * Availability: https://github.com/szimek/signature_pad
43891 * Create a new BezierSignature
43892 * @param {Object} config The config object
43895 Roo.bootstrap.BezierSignature = function(config){
43896 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43902 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43909 mouse_btn_down: true,
43912 * @cfg {int} canvas height
43914 canvas_height: '200px',
43917 * @cfg {float|function} Radius of a single dot.
43922 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43927 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43932 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43937 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43942 * @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.
43944 bg_color: 'rgba(0, 0, 0, 0)',
43947 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43949 dot_color: 'black',
43952 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43954 velocity_filter_weight: 0.7,
43957 * @cfg {function} Callback when stroke begin.
43962 * @cfg {function} Callback when stroke end.
43966 getAutoCreate : function()
43968 var cls = 'roo-signature column';
43971 cls += ' ' + this.cls;
43981 for(var i = 0; i < col_sizes.length; i++) {
43982 if(this[col_sizes[i]]) {
43983 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43993 cls: 'roo-signature-body',
43997 cls: 'roo-signature-body-canvas',
43998 height: this.canvas_height,
43999 width: this.canvas_width
44006 style: 'display: none'
44014 initEvents: function()
44016 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44018 var canvas = this.canvasEl();
44020 // mouse && touch event swapping...
44021 canvas.dom.style.touchAction = 'none';
44022 canvas.dom.style.msTouchAction = 'none';
44024 this.mouse_btn_down = false;
44025 canvas.on('mousedown', this._handleMouseDown, this);
44026 canvas.on('mousemove', this._handleMouseMove, this);
44027 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44029 if (window.PointerEvent) {
44030 canvas.on('pointerdown', this._handleMouseDown, this);
44031 canvas.on('pointermove', this._handleMouseMove, this);
44032 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44035 if ('ontouchstart' in window) {
44036 canvas.on('touchstart', this._handleTouchStart, this);
44037 canvas.on('touchmove', this._handleTouchMove, this);
44038 canvas.on('touchend', this._handleTouchEnd, this);
44041 Roo.EventManager.onWindowResize(this.resize, this, true);
44043 // file input event
44044 this.fileEl().on('change', this.uploadImage, this);
44051 resize: function(){
44053 var canvas = this.canvasEl().dom;
44054 var ctx = this.canvasElCtx();
44055 var img_data = false;
44057 if(canvas.width > 0) {
44058 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44060 // setting canvas width will clean img data
44063 var style = window.getComputedStyle ?
44064 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44066 var padding_left = parseInt(style.paddingLeft) || 0;
44067 var padding_right = parseInt(style.paddingRight) || 0;
44069 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44072 ctx.putImageData(img_data, 0, 0);
44076 _handleMouseDown: function(e)
44078 if (e.browserEvent.which === 1) {
44079 this.mouse_btn_down = true;
44080 this.strokeBegin(e);
44084 _handleMouseMove: function (e)
44086 if (this.mouse_btn_down) {
44087 this.strokeMoveUpdate(e);
44091 _handleMouseUp: function (e)
44093 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44094 this.mouse_btn_down = false;
44099 _handleTouchStart: function (e) {
44101 e.preventDefault();
44102 if (e.browserEvent.targetTouches.length === 1) {
44103 // var touch = e.browserEvent.changedTouches[0];
44104 // this.strokeBegin(touch);
44106 this.strokeBegin(e); // assume e catching the correct xy...
44110 _handleTouchMove: function (e) {
44111 e.preventDefault();
44112 // var touch = event.targetTouches[0];
44113 // _this._strokeMoveUpdate(touch);
44114 this.strokeMoveUpdate(e);
44117 _handleTouchEnd: function (e) {
44118 var wasCanvasTouched = e.target === this.canvasEl().dom;
44119 if (wasCanvasTouched) {
44120 e.preventDefault();
44121 // var touch = event.changedTouches[0];
44122 // _this._strokeEnd(touch);
44127 reset: function () {
44128 this._lastPoints = [];
44129 this._lastVelocity = 0;
44130 this._lastWidth = (this.min_width + this.max_width) / 2;
44131 this.canvasElCtx().fillStyle = this.dot_color;
44134 strokeMoveUpdate: function(e)
44136 this.strokeUpdate(e);
44138 if (this.throttle) {
44139 this.throttleStroke(this.strokeUpdate, this.throttle);
44142 this.strokeUpdate(e);
44146 strokeBegin: function(e)
44148 var newPointGroup = {
44149 color: this.dot_color,
44153 if (typeof this.onBegin === 'function') {
44157 this.curve_data.push(newPointGroup);
44159 this.strokeUpdate(e);
44162 strokeUpdate: function(e)
44164 var rect = this.canvasEl().dom.getBoundingClientRect();
44165 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44166 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44167 var lastPoints = lastPointGroup.points;
44168 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44169 var isLastPointTooClose = lastPoint
44170 ? point.distanceTo(lastPoint) <= this.min_distance
44172 var color = lastPointGroup.color;
44173 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44174 var curve = this.addPoint(point);
44176 this.drawDot({color: color, point: point});
44179 this.drawCurve({color: color, curve: curve});
44189 strokeEnd: function(e)
44191 this.strokeUpdate(e);
44192 if (typeof this.onEnd === 'function') {
44197 addPoint: function (point) {
44198 var _lastPoints = this._lastPoints;
44199 _lastPoints.push(point);
44200 if (_lastPoints.length > 2) {
44201 if (_lastPoints.length === 3) {
44202 _lastPoints.unshift(_lastPoints[0]);
44204 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44205 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44206 _lastPoints.shift();
44212 calculateCurveWidths: function (startPoint, endPoint) {
44213 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44214 (1 - this.velocity_filter_weight) * this._lastVelocity;
44216 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44219 start: this._lastWidth
44222 this._lastVelocity = velocity;
44223 this._lastWidth = newWidth;
44227 drawDot: function (_a) {
44228 var color = _a.color, point = _a.point;
44229 var ctx = this.canvasElCtx();
44230 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44232 this.drawCurveSegment(point.x, point.y, width);
44234 ctx.fillStyle = color;
44238 drawCurve: function (_a) {
44239 var color = _a.color, curve = _a.curve;
44240 var ctx = this.canvasElCtx();
44241 var widthDelta = curve.endWidth - curve.startWidth;
44242 var drawSteps = Math.floor(curve.length()) * 2;
44244 ctx.fillStyle = color;
44245 for (var i = 0; i < drawSteps; i += 1) {
44246 var t = i / drawSteps;
44252 var x = uuu * curve.startPoint.x;
44253 x += 3 * uu * t * curve.control1.x;
44254 x += 3 * u * tt * curve.control2.x;
44255 x += ttt * curve.endPoint.x;
44256 var y = uuu * curve.startPoint.y;
44257 y += 3 * uu * t * curve.control1.y;
44258 y += 3 * u * tt * curve.control2.y;
44259 y += ttt * curve.endPoint.y;
44260 var width = curve.startWidth + ttt * widthDelta;
44261 this.drawCurveSegment(x, y, width);
44267 drawCurveSegment: function (x, y, width) {
44268 var ctx = this.canvasElCtx();
44270 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44271 this.is_empty = false;
44276 var ctx = this.canvasElCtx();
44277 var canvas = this.canvasEl().dom;
44278 ctx.fillStyle = this.bg_color;
44279 ctx.clearRect(0, 0, canvas.width, canvas.height);
44280 ctx.fillRect(0, 0, canvas.width, canvas.height);
44281 this.curve_data = [];
44283 this.is_empty = true;
44288 return this.el.select('input',true).first();
44291 canvasEl: function()
44293 return this.el.select('canvas',true).first();
44296 canvasElCtx: function()
44298 return this.el.select('canvas',true).first().dom.getContext('2d');
44301 getImage: function(type)
44303 if(this.is_empty) {
44308 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44311 drawFromImage: function(img_src)
44313 var img = new Image();
44315 img.onload = function(){
44316 this.canvasElCtx().drawImage(img, 0, 0);
44321 this.is_empty = false;
44324 selectImage: function()
44326 this.fileEl().dom.click();
44329 uploadImage: function(e)
44331 var reader = new FileReader();
44333 reader.onload = function(e){
44334 var img = new Image();
44335 img.onload = function(){
44337 this.canvasElCtx().drawImage(img, 0, 0);
44339 img.src = e.target.result;
44342 reader.readAsDataURL(e.target.files[0]);
44345 // Bezier Point Constructor
44346 Point: (function () {
44347 function Point(x, y, time) {
44350 this.time = time || Date.now();
44352 Point.prototype.distanceTo = function (start) {
44353 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44355 Point.prototype.equals = function (other) {
44356 return this.x === other.x && this.y === other.y && this.time === other.time;
44358 Point.prototype.velocityFrom = function (start) {
44359 return this.time !== start.time
44360 ? this.distanceTo(start) / (this.time - start.time)
44367 // Bezier Constructor
44368 Bezier: (function () {
44369 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44370 this.startPoint = startPoint;
44371 this.control2 = control2;
44372 this.control1 = control1;
44373 this.endPoint = endPoint;
44374 this.startWidth = startWidth;
44375 this.endWidth = endWidth;
44377 Bezier.fromPoints = function (points, widths, scope) {
44378 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44379 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44380 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44382 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44383 var dx1 = s1.x - s2.x;
44384 var dy1 = s1.y - s2.y;
44385 var dx2 = s2.x - s3.x;
44386 var dy2 = s2.y - s3.y;
44387 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44388 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44389 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44390 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44391 var dxm = m1.x - m2.x;
44392 var dym = m1.y - m2.y;
44393 var k = l2 / (l1 + l2);
44394 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44395 var tx = s2.x - cm.x;
44396 var ty = s2.y - cm.y;
44398 c1: new scope.Point(m1.x + tx, m1.y + ty),
44399 c2: new scope.Point(m2.x + tx, m2.y + ty)
44402 Bezier.prototype.length = function () {
44407 for (var i = 0; i <= steps; i += 1) {
44409 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44410 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44412 var xdiff = cx - px;
44413 var ydiff = cy - py;
44414 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44421 Bezier.prototype.point = function (t, start, c1, c2, end) {
44422 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44423 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44424 + (3.0 * c2 * (1.0 - t) * t * t)
44425 + (end * t * t * t);
44430 throttleStroke: function(fn, wait) {
44431 if (wait === void 0) { wait = 250; }
44433 var timeout = null;
44437 var later = function () {
44438 previous = Date.now();
44440 result = fn.apply(storedContext, storedArgs);
44442 storedContext = null;
44446 return function wrapper() {
44448 for (var _i = 0; _i < arguments.length; _i++) {
44449 args[_i] = arguments[_i];
44451 var now = Date.now();
44452 var remaining = wait - (now - previous);
44453 storedContext = this;
44455 if (remaining <= 0 || remaining > wait) {
44457 clearTimeout(timeout);
44461 result = fn.apply(storedContext, storedArgs);
44463 storedContext = null;
44467 else if (!timeout) {
44468 timeout = window.setTimeout(later, remaining);