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
12532 value : this.value,
12533 cls : 'd-none form-control'
12538 multiple : 'multiple',
12540 cls : 'd-none roo-card-upload-selector'
12544 cls : 'roo-card-uploader-button-container w-100 mb-2'
12547 cls : 'card-columns roo-card-uploader-container'
12557 getChildContainer : function() /// what children are added to.
12559 return this.containerEl;
12562 getButtonContainer : function() /// what children are added to.
12564 return this.el.select(".roo-card-uploader-button-container").first();
12567 initEvents : function()
12570 Roo.bootstrap.Input.prototype.initEvents.call(this);
12574 xns: Roo.bootstrap,
12577 container_method : 'getButtonContainer' ,
12578 html : this.html, // fix changable?
12581 'click' : function(btn, e) {
12590 this.urlAPI = (window.createObjectURL && window) ||
12591 (window.URL && URL.revokeObjectURL && URL) ||
12592 (window.webkitURL && webkitURL);
12597 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12599 this.selectorEl.on('change', this.onFileSelected, this);
12602 this.images.forEach(function(img) {
12605 this.images = false;
12607 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12613 onClick : function(e)
12615 e.preventDefault();
12617 this.selectorEl.dom.click();
12621 onFileSelected : function(e)
12623 e.preventDefault();
12625 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12629 Roo.each(this.selectorEl.dom.files, function(file){
12630 this.addFile(file);
12639 addFile : function(file)
12642 if(typeof(file) === 'string'){
12643 throw "Add file by name?"; // should not happen
12647 if(!file || !this.urlAPI){
12657 var url = _this.urlAPI.createObjectURL( file);
12660 id : Roo.bootstrap.CardUploader.ID--,
12661 is_uploaded : false,
12664 mimetype : file.type,
12671 addCard : function (data)
12673 // hidden input element?
12674 // if the file is not an image...
12675 //then we need to use something other that and header_image
12680 xns : Roo.bootstrap,
12681 xtype : 'CardFooter',
12684 xns : Roo.bootstrap,
12690 xns : Roo.bootstrap,
12692 html : String.format("<small>{0}</small>", data.title),
12693 cls : 'col-11 text-left',
12698 click : function() {
12699 this.downloadCard(data.id)
12705 xns : Roo.bootstrap,
12713 click : function() {
12714 t.removeCard(data.id)
12726 var cn = this.addxtype(
12729 xns : Roo.bootstrap,
12732 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12734 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12739 initEvents : function() {
12740 Roo.bootstrap.Card.prototype.initEvents.call(this);
12741 this.imgEl = this.el.select('.card-img-top').first();
12743 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744 this.imgEl.set({ 'pointer' : 'cursor' });
12753 // dont' really need ot update items.
12754 // this.items.push(cn);
12755 this.fileCollection.add(cn);
12758 var reader = new FileReader();
12759 reader.onloadend = function(evt) {
12760 data.srcdata = evt.target.result;
12763 reader.readAsDataURL(data.src);
12768 removeCard : function(id)
12771 var card = this.fileCollection.get(id);
12772 card.data.is_deleted = 1;
12773 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12774 this.fileCollection.remove(card);
12775 //this.items = this.items.filter(function(e) { return e != card });
12776 // dont' really need ot update items.
12777 card.el.dom.parentNode.removeChild(card.el.dom);
12782 this.fileCollection.each(function(card) {
12783 card.el.dom.parentNode.removeChild(card.el.dom);
12785 this.fileCollection.clear();
12786 this.updateInput();
12789 updateInput : function()
12793 this.fileCollection.forEach(function(e) {
12797 this.inputEl().dom.value = JSON.stringify(data);
12807 Roo.bootstrap.CardUploader.ID = -1;/*
12809 * Ext JS Library 1.1.1
12810 * Copyright(c) 2006-2007, Ext JS, LLC.
12812 * Originally Released Under LGPL - original licence link has changed is not relivant.
12815 * <script type="text/javascript">
12820 * @class Roo.data.SortTypes
12822 * Defines the default sorting (casting?) comparison functions used when sorting data.
12824 Roo.data.SortTypes = {
12826 * Default sort that does nothing
12827 * @param {Mixed} s The value being converted
12828 * @return {Mixed} The comparison value
12830 none : function(s){
12835 * The regular expression used to strip tags
12839 stripTagsRE : /<\/?[^>]+>/gi,
12842 * Strips all HTML tags to sort on text only
12843 * @param {Mixed} s The value being converted
12844 * @return {String} The comparison value
12846 asText : function(s){
12847 return String(s).replace(this.stripTagsRE, "");
12851 * Strips all HTML tags to sort on text only - Case insensitive
12852 * @param {Mixed} s The value being converted
12853 * @return {String} The comparison value
12855 asUCText : function(s){
12856 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12860 * Case insensitive string
12861 * @param {Mixed} s The value being converted
12862 * @return {String} The comparison value
12864 asUCString : function(s) {
12865 return String(s).toUpperCase();
12870 * @param {Mixed} s The value being converted
12871 * @return {Number} The comparison value
12873 asDate : function(s) {
12877 if(s instanceof Date){
12878 return s.getTime();
12880 return Date.parse(String(s));
12885 * @param {Mixed} s The value being converted
12886 * @return {Float} The comparison value
12888 asFloat : function(s) {
12889 var val = parseFloat(String(s).replace(/,/g, ""));
12898 * @param {Mixed} s The value being converted
12899 * @return {Number} The comparison value
12901 asInt : function(s) {
12902 var val = parseInt(String(s).replace(/,/g, ""));
12910 * Ext JS Library 1.1.1
12911 * Copyright(c) 2006-2007, Ext JS, LLC.
12913 * Originally Released Under LGPL - original licence link has changed is not relivant.
12916 * <script type="text/javascript">
12920 * @class Roo.data.Record
12921 * Instances of this class encapsulate both record <em>definition</em> information, and record
12922 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12923 * to access Records cached in an {@link Roo.data.Store} object.<br>
12925 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12926 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12929 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12931 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12932 * {@link #create}. The parameters are the same.
12933 * @param {Array} data An associative Array of data values keyed by the field name.
12934 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12935 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12936 * not specified an integer id is generated.
12938 Roo.data.Record = function(data, id){
12939 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12944 * Generate a constructor for a specific record layout.
12945 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12946 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12947 * Each field definition object may contain the following properties: <ul>
12948 * <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,
12949 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12950 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12951 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12952 * is being used, then this is a string containing the javascript expression to reference the data relative to
12953 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12954 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12955 * this may be omitted.</p></li>
12956 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12957 * <ul><li>auto (Default, implies no conversion)</li>
12962 * <li>date</li></ul></p></li>
12963 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12964 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12965 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12966 * by the Reader into an object that will be stored in the Record. It is passed the
12967 * following parameters:<ul>
12968 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12970 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12972 * <br>usage:<br><pre><code>
12973 var TopicRecord = Roo.data.Record.create(
12974 {name: 'title', mapping: 'topic_title'},
12975 {name: 'author', mapping: 'username'},
12976 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12977 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12978 {name: 'lastPoster', mapping: 'user2'},
12979 {name: 'excerpt', mapping: 'post_text'}
12982 var myNewRecord = new TopicRecord({
12983 title: 'Do my job please',
12986 lastPost: new Date(),
12987 lastPoster: 'Animal',
12988 excerpt: 'No way dude!'
12990 myStore.add(myNewRecord);
12995 Roo.data.Record.create = function(o){
12996 var f = function(){
12997 f.superclass.constructor.apply(this, arguments);
12999 Roo.extend(f, Roo.data.Record);
13000 var p = f.prototype;
13001 p.fields = new Roo.util.MixedCollection(false, function(field){
13004 for(var i = 0, len = o.length; i < len; i++){
13005 p.fields.add(new Roo.data.Field(o[i]));
13007 f.getField = function(name){
13008 return p.fields.get(name);
13013 Roo.data.Record.AUTO_ID = 1000;
13014 Roo.data.Record.EDIT = 'edit';
13015 Roo.data.Record.REJECT = 'reject';
13016 Roo.data.Record.COMMIT = 'commit';
13018 Roo.data.Record.prototype = {
13020 * Readonly flag - true if this record has been modified.
13029 join : function(store){
13030 this.store = store;
13034 * Set the named field to the specified value.
13035 * @param {String} name The name of the field to set.
13036 * @param {Object} value The value to set the field to.
13038 set : function(name, value){
13039 if(this.data[name] == value){
13043 if(!this.modified){
13044 this.modified = {};
13046 if(typeof this.modified[name] == 'undefined'){
13047 this.modified[name] = this.data[name];
13049 this.data[name] = value;
13050 if(!this.editing && this.store){
13051 this.store.afterEdit(this);
13056 * Get the value of the named field.
13057 * @param {String} name The name of the field to get the value of.
13058 * @return {Object} The value of the field.
13060 get : function(name){
13061 return this.data[name];
13065 beginEdit : function(){
13066 this.editing = true;
13067 this.modified = {};
13071 cancelEdit : function(){
13072 this.editing = false;
13073 delete this.modified;
13077 endEdit : function(){
13078 this.editing = false;
13079 if(this.dirty && this.store){
13080 this.store.afterEdit(this);
13085 * Usually called by the {@link Roo.data.Store} which owns the Record.
13086 * Rejects all changes made to the Record since either creation, or the last commit operation.
13087 * Modified fields are reverted to their original values.
13089 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13090 * of reject operations.
13092 reject : function(){
13093 var m = this.modified;
13095 if(typeof m[n] != "function"){
13096 this.data[n] = m[n];
13099 this.dirty = false;
13100 delete this.modified;
13101 this.editing = false;
13103 this.store.afterReject(this);
13108 * Usually called by the {@link Roo.data.Store} which owns the Record.
13109 * Commits all changes made to the Record since either creation, or the last commit operation.
13111 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13112 * of commit operations.
13114 commit : function(){
13115 this.dirty = false;
13116 delete this.modified;
13117 this.editing = false;
13119 this.store.afterCommit(this);
13124 hasError : function(){
13125 return this.error != null;
13129 clearError : function(){
13134 * Creates a copy of this record.
13135 * @param {String} id (optional) A new record id if you don't want to use this record's id
13138 copy : function(newId) {
13139 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13143 * Ext JS Library 1.1.1
13144 * Copyright(c) 2006-2007, Ext JS, LLC.
13146 * Originally Released Under LGPL - original licence link has changed is not relivant.
13149 * <script type="text/javascript">
13155 * @class Roo.data.Store
13156 * @extends Roo.util.Observable
13157 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13158 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13160 * 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
13161 * has no knowledge of the format of the data returned by the Proxy.<br>
13163 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13164 * instances from the data object. These records are cached and made available through accessor functions.
13166 * Creates a new Store.
13167 * @param {Object} config A config object containing the objects needed for the Store to access data,
13168 * and read the data into Records.
13170 Roo.data.Store = function(config){
13171 this.data = new Roo.util.MixedCollection(false);
13172 this.data.getKey = function(o){
13175 this.baseParams = {};
13177 this.paramNames = {
13182 "multisort" : "_multisort"
13185 if(config && config.data){
13186 this.inlineData = config.data;
13187 delete config.data;
13190 Roo.apply(this, config);
13192 if(this.reader){ // reader passed
13193 this.reader = Roo.factory(this.reader, Roo.data);
13194 this.reader.xmodule = this.xmodule || false;
13195 if(!this.recordType){
13196 this.recordType = this.reader.recordType;
13198 if(this.reader.onMetaChange){
13199 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13203 if(this.recordType){
13204 this.fields = this.recordType.prototype.fields;
13206 this.modified = [];
13210 * @event datachanged
13211 * Fires when the data cache has changed, and a widget which is using this Store
13212 * as a Record cache should refresh its view.
13213 * @param {Store} this
13215 datachanged : true,
13217 * @event metachange
13218 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13219 * @param {Store} this
13220 * @param {Object} meta The JSON metadata
13225 * Fires when Records have been added to the Store
13226 * @param {Store} this
13227 * @param {Roo.data.Record[]} records The array of Records added
13228 * @param {Number} index The index at which the record(s) were added
13233 * Fires when a Record has been removed from the Store
13234 * @param {Store} this
13235 * @param {Roo.data.Record} record The Record that was removed
13236 * @param {Number} index The index at which the record was removed
13241 * Fires when a Record has been updated
13242 * @param {Store} this
13243 * @param {Roo.data.Record} record The Record that was updated
13244 * @param {String} operation The update operation being performed. Value may be one of:
13246 Roo.data.Record.EDIT
13247 Roo.data.Record.REJECT
13248 Roo.data.Record.COMMIT
13254 * Fires when the data cache has been cleared.
13255 * @param {Store} this
13259 * @event beforeload
13260 * Fires before a request is made for a new data object. If the beforeload handler returns false
13261 * the load action will be canceled.
13262 * @param {Store} this
13263 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13267 * @event beforeloadadd
13268 * Fires after a new set of Records has been loaded.
13269 * @param {Store} this
13270 * @param {Roo.data.Record[]} records The Records that were loaded
13271 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13273 beforeloadadd : true,
13276 * Fires after a new set of Records has been loaded, before they are added to the store.
13277 * @param {Store} this
13278 * @param {Roo.data.Record[]} records The Records that were loaded
13279 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13280 * @params {Object} return from reader
13284 * @event loadexception
13285 * Fires if an exception occurs in the Proxy during loading.
13286 * Called with the signature of the Proxy's "loadexception" event.
13287 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13290 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13291 * @param {Object} load options
13292 * @param {Object} jsonData from your request (normally this contains the Exception)
13294 loadexception : true
13298 this.proxy = Roo.factory(this.proxy, Roo.data);
13299 this.proxy.xmodule = this.xmodule || false;
13300 this.relayEvents(this.proxy, ["loadexception"]);
13302 this.sortToggle = {};
13303 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13305 Roo.data.Store.superclass.constructor.call(this);
13307 if(this.inlineData){
13308 this.loadData(this.inlineData);
13309 delete this.inlineData;
13313 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13315 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13316 * without a remote query - used by combo/forms at present.
13320 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13323 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13326 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13327 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13330 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13331 * on any HTTP request
13334 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13337 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13341 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13342 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13344 remoteSort : false,
13347 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13348 * loaded or when a record is removed. (defaults to false).
13350 pruneModifiedRecords : false,
13353 lastOptions : null,
13356 * Add Records to the Store and fires the add event.
13357 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13359 add : function(records){
13360 records = [].concat(records);
13361 for(var i = 0, len = records.length; i < len; i++){
13362 records[i].join(this);
13364 var index = this.data.length;
13365 this.data.addAll(records);
13366 this.fireEvent("add", this, records, index);
13370 * Remove a Record from the Store and fires the remove event.
13371 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13373 remove : function(record){
13374 var index = this.data.indexOf(record);
13375 this.data.removeAt(index);
13377 if(this.pruneModifiedRecords){
13378 this.modified.remove(record);
13380 this.fireEvent("remove", this, record, index);
13384 * Remove all Records from the Store and fires the clear event.
13386 removeAll : function(){
13388 if(this.pruneModifiedRecords){
13389 this.modified = [];
13391 this.fireEvent("clear", this);
13395 * Inserts Records to the Store at the given index and fires the add event.
13396 * @param {Number} index The start index at which to insert the passed Records.
13397 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13399 insert : function(index, records){
13400 records = [].concat(records);
13401 for(var i = 0, len = records.length; i < len; i++){
13402 this.data.insert(index, records[i]);
13403 records[i].join(this);
13405 this.fireEvent("add", this, records, index);
13409 * Get the index within the cache of the passed Record.
13410 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13411 * @return {Number} The index of the passed Record. Returns -1 if not found.
13413 indexOf : function(record){
13414 return this.data.indexOf(record);
13418 * Get the index within the cache of the Record with the passed id.
13419 * @param {String} id The id of the Record to find.
13420 * @return {Number} The index of the Record. Returns -1 if not found.
13422 indexOfId : function(id){
13423 return this.data.indexOfKey(id);
13427 * Get the Record with the specified id.
13428 * @param {String} id The id of the Record to find.
13429 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13431 getById : function(id){
13432 return this.data.key(id);
13436 * Get the Record at the specified index.
13437 * @param {Number} index The index of the Record to find.
13438 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13440 getAt : function(index){
13441 return this.data.itemAt(index);
13445 * Returns a range of Records between specified indices.
13446 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13447 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13448 * @return {Roo.data.Record[]} An array of Records
13450 getRange : function(start, end){
13451 return this.data.getRange(start, end);
13455 storeOptions : function(o){
13456 o = Roo.apply({}, o);
13459 this.lastOptions = o;
13463 * Loads the Record cache from the configured Proxy using the configured Reader.
13465 * If using remote paging, then the first load call must specify the <em>start</em>
13466 * and <em>limit</em> properties in the options.params property to establish the initial
13467 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13469 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13470 * and this call will return before the new data has been loaded. Perform any post-processing
13471 * in a callback function, or in a "load" event handler.</strong>
13473 * @param {Object} options An object containing properties which control loading options:<ul>
13474 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13475 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13476 * passed the following arguments:<ul>
13477 * <li>r : Roo.data.Record[]</li>
13478 * <li>options: Options object from the load call</li>
13479 * <li>success: Boolean success indicator</li></ul></li>
13480 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13481 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13484 load : function(options){
13485 options = options || {};
13486 if(this.fireEvent("beforeload", this, options) !== false){
13487 this.storeOptions(options);
13488 var p = Roo.apply(options.params || {}, this.baseParams);
13489 // if meta was not loaded from remote source.. try requesting it.
13490 if (!this.reader.metaFromRemote) {
13491 p._requestMeta = 1;
13493 if(this.sortInfo && this.remoteSort){
13494 var pn = this.paramNames;
13495 p[pn["sort"]] = this.sortInfo.field;
13496 p[pn["dir"]] = this.sortInfo.direction;
13498 if (this.multiSort) {
13499 var pn = this.paramNames;
13500 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13503 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13508 * Reloads the Record cache from the configured Proxy using the configured Reader and
13509 * the options from the last load operation performed.
13510 * @param {Object} options (optional) An object containing properties which may override the options
13511 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13512 * the most recently used options are reused).
13514 reload : function(options){
13515 this.load(Roo.applyIf(options||{}, this.lastOptions));
13519 // Called as a callback by the Reader during a load operation.
13520 loadRecords : function(o, options, success){
13521 if(!o || success === false){
13522 if(success !== false){
13523 this.fireEvent("load", this, [], options, o);
13525 if(options.callback){
13526 options.callback.call(options.scope || this, [], options, false);
13530 // if data returned failure - throw an exception.
13531 if (o.success === false) {
13532 // show a message if no listener is registered.
13533 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13534 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13536 // loadmask wil be hooked into this..
13537 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13540 var r = o.records, t = o.totalRecords || r.length;
13542 this.fireEvent("beforeloadadd", this, r, options, o);
13544 if(!options || options.add !== true){
13545 if(this.pruneModifiedRecords){
13546 this.modified = [];
13548 for(var i = 0, len = r.length; i < len; i++){
13552 this.data = this.snapshot;
13553 delete this.snapshot;
13556 this.data.addAll(r);
13557 this.totalLength = t;
13559 this.fireEvent("datachanged", this);
13561 this.totalLength = Math.max(t, this.data.length+r.length);
13565 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13567 var e = new Roo.data.Record({});
13569 e.set(this.parent.displayField, this.parent.emptyTitle);
13570 e.set(this.parent.valueField, '');
13575 this.fireEvent("load", this, r, options, o);
13576 if(options.callback){
13577 options.callback.call(options.scope || this, r, options, true);
13583 * Loads data from a passed data block. A Reader which understands the format of the data
13584 * must have been configured in the constructor.
13585 * @param {Object} data The data block from which to read the Records. The format of the data expected
13586 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13587 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13589 loadData : function(o, append){
13590 var r = this.reader.readRecords(o);
13591 this.loadRecords(r, {add: append}, true);
13595 * using 'cn' the nested child reader read the child array into it's child stores.
13596 * @param {Object} rec The record with a 'children array
13598 loadDataFromChildren : function(rec)
13600 this.loadData(this.reader.toLoadData(rec));
13605 * Gets the number of cached records.
13607 * <em>If using paging, this may not be the total size of the dataset. If the data object
13608 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13609 * the data set size</em>
13611 getCount : function(){
13612 return this.data.length || 0;
13616 * Gets the total number of records in the dataset as returned by the server.
13618 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13619 * the dataset size</em>
13621 getTotalCount : function(){
13622 return this.totalLength || 0;
13626 * Returns the sort state of the Store as an object with two properties:
13628 field {String} The name of the field by which the Records are sorted
13629 direction {String} The sort order, "ASC" or "DESC"
13632 getSortState : function(){
13633 return this.sortInfo;
13637 applySort : function(){
13638 if(this.sortInfo && !this.remoteSort){
13639 var s = this.sortInfo, f = s.field;
13640 var st = this.fields.get(f).sortType;
13641 var fn = function(r1, r2){
13642 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13643 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13645 this.data.sort(s.direction, fn);
13646 if(this.snapshot && this.snapshot != this.data){
13647 this.snapshot.sort(s.direction, fn);
13653 * Sets the default sort column and order to be used by the next load operation.
13654 * @param {String} fieldName The name of the field to sort by.
13655 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13657 setDefaultSort : function(field, dir){
13658 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13662 * Sort the Records.
13663 * If remote sorting is used, the sort is performed on the server, and the cache is
13664 * reloaded. If local sorting is used, the cache is sorted internally.
13665 * @param {String} fieldName The name of the field to sort by.
13666 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13668 sort : function(fieldName, dir){
13669 var f = this.fields.get(fieldName);
13671 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13673 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13674 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13679 this.sortToggle[f.name] = dir;
13680 this.sortInfo = {field: f.name, direction: dir};
13681 if(!this.remoteSort){
13683 this.fireEvent("datachanged", this);
13685 this.load(this.lastOptions);
13690 * Calls the specified function for each of the Records in the cache.
13691 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13692 * Returning <em>false</em> aborts and exits the iteration.
13693 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13695 each : function(fn, scope){
13696 this.data.each(fn, scope);
13700 * Gets all records modified since the last commit. Modified records are persisted across load operations
13701 * (e.g., during paging).
13702 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13704 getModifiedRecords : function(){
13705 return this.modified;
13709 createFilterFn : function(property, value, anyMatch){
13710 if(!value.exec){ // not a regex
13711 value = String(value);
13712 if(value.length == 0){
13715 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13717 return function(r){
13718 return value.test(r.data[property]);
13723 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13724 * @param {String} property A field on your records
13725 * @param {Number} start The record index to start at (defaults to 0)
13726 * @param {Number} end The last record index to include (defaults to length - 1)
13727 * @return {Number} The sum
13729 sum : function(property, start, end){
13730 var rs = this.data.items, v = 0;
13731 start = start || 0;
13732 end = (end || end === 0) ? end : rs.length-1;
13734 for(var i = start; i <= end; i++){
13735 v += (rs[i].data[property] || 0);
13741 * Filter the records by a specified property.
13742 * @param {String} field A field on your records
13743 * @param {String/RegExp} value Either a string that the field
13744 * should start with or a RegExp to test against the field
13745 * @param {Boolean} anyMatch True to match any part not just the beginning
13747 filter : function(property, value, anyMatch){
13748 var fn = this.createFilterFn(property, value, anyMatch);
13749 return fn ? this.filterBy(fn) : this.clearFilter();
13753 * Filter by a function. The specified function will be called with each
13754 * record in this data source. If the function returns true the record is included,
13755 * otherwise it is filtered.
13756 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13757 * @param {Object} scope (optional) The scope of the function (defaults to this)
13759 filterBy : function(fn, scope){
13760 this.snapshot = this.snapshot || this.data;
13761 this.data = this.queryBy(fn, scope||this);
13762 this.fireEvent("datachanged", this);
13766 * Query the records by a specified property.
13767 * @param {String} field A field on your records
13768 * @param {String/RegExp} value Either a string that the field
13769 * should start with or a RegExp to test against the field
13770 * @param {Boolean} anyMatch True to match any part not just the beginning
13771 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13773 query : function(property, value, anyMatch){
13774 var fn = this.createFilterFn(property, value, anyMatch);
13775 return fn ? this.queryBy(fn) : this.data.clone();
13779 * Query by a function. The specified function will be called with each
13780 * record in this data source. If the function returns true the record is included
13782 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13783 * @param {Object} scope (optional) The scope of the function (defaults to this)
13784 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13786 queryBy : function(fn, scope){
13787 var data = this.snapshot || this.data;
13788 return data.filterBy(fn, scope||this);
13792 * Collects unique values for a particular dataIndex from this store.
13793 * @param {String} dataIndex The property to collect
13794 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13795 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13796 * @return {Array} An array of the unique values
13798 collect : function(dataIndex, allowNull, bypassFilter){
13799 var d = (bypassFilter === true && this.snapshot) ?
13800 this.snapshot.items : this.data.items;
13801 var v, sv, r = [], l = {};
13802 for(var i = 0, len = d.length; i < len; i++){
13803 v = d[i].data[dataIndex];
13805 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13814 * Revert to a view of the Record cache with no filtering applied.
13815 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13817 clearFilter : function(suppressEvent){
13818 if(this.snapshot && this.snapshot != this.data){
13819 this.data = this.snapshot;
13820 delete this.snapshot;
13821 if(suppressEvent !== true){
13822 this.fireEvent("datachanged", this);
13828 afterEdit : function(record){
13829 if(this.modified.indexOf(record) == -1){
13830 this.modified.push(record);
13832 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13836 afterReject : function(record){
13837 this.modified.remove(record);
13838 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13842 afterCommit : function(record){
13843 this.modified.remove(record);
13844 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13848 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13849 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13851 commitChanges : function(){
13852 var m = this.modified.slice(0);
13853 this.modified = [];
13854 for(var i = 0, len = m.length; i < len; i++){
13860 * Cancel outstanding changes on all changed records.
13862 rejectChanges : function(){
13863 var m = this.modified.slice(0);
13864 this.modified = [];
13865 for(var i = 0, len = m.length; i < len; i++){
13870 onMetaChange : function(meta, rtype, o){
13871 this.recordType = rtype;
13872 this.fields = rtype.prototype.fields;
13873 delete this.snapshot;
13874 this.sortInfo = meta.sortInfo || this.sortInfo;
13875 this.modified = [];
13876 this.fireEvent('metachange', this, this.reader.meta);
13879 moveIndex : function(data, type)
13881 var index = this.indexOf(data);
13883 var newIndex = index + type;
13887 this.insert(newIndex, data);
13892 * Ext JS Library 1.1.1
13893 * Copyright(c) 2006-2007, Ext JS, LLC.
13895 * Originally Released Under LGPL - original licence link has changed is not relivant.
13898 * <script type="text/javascript">
13902 * @class Roo.data.SimpleStore
13903 * @extends Roo.data.Store
13904 * Small helper class to make creating Stores from Array data easier.
13905 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13906 * @cfg {Array} fields An array of field definition objects, or field name strings.
13907 * @cfg {Object} an existing reader (eg. copied from another store)
13908 * @cfg {Array} data The multi-dimensional array of data
13910 * @param {Object} config
13912 Roo.data.SimpleStore = function(config)
13914 Roo.data.SimpleStore.superclass.constructor.call(this, {
13916 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13919 Roo.data.Record.create(config.fields)
13921 proxy : new Roo.data.MemoryProxy(config.data)
13925 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13927 * Ext JS Library 1.1.1
13928 * Copyright(c) 2006-2007, Ext JS, LLC.
13930 * Originally Released Under LGPL - original licence link has changed is not relivant.
13933 * <script type="text/javascript">
13938 * @extends Roo.data.Store
13939 * @class Roo.data.JsonStore
13940 * Small helper class to make creating Stores for JSON data easier. <br/>
13942 var store = new Roo.data.JsonStore({
13943 url: 'get-images.php',
13945 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13948 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13949 * JsonReader and HttpProxy (unless inline data is provided).</b>
13950 * @cfg {Array} fields An array of field definition objects, or field name strings.
13952 * @param {Object} config
13954 Roo.data.JsonStore = function(c){
13955 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13956 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13957 reader: new Roo.data.JsonReader(c, c.fields)
13960 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13962 * Ext JS Library 1.1.1
13963 * Copyright(c) 2006-2007, Ext JS, LLC.
13965 * Originally Released Under LGPL - original licence link has changed is not relivant.
13968 * <script type="text/javascript">
13972 Roo.data.Field = function(config){
13973 if(typeof config == "string"){
13974 config = {name: config};
13976 Roo.apply(this, config);
13979 this.type = "auto";
13982 var st = Roo.data.SortTypes;
13983 // named sortTypes are supported, here we look them up
13984 if(typeof this.sortType == "string"){
13985 this.sortType = st[this.sortType];
13988 // set default sortType for strings and dates
13989 if(!this.sortType){
13992 this.sortType = st.asUCString;
13995 this.sortType = st.asDate;
13998 this.sortType = st.none;
14003 var stripRe = /[\$,%]/g;
14005 // prebuilt conversion function for this field, instead of
14006 // switching every time we're reading a value
14008 var cv, dateFormat = this.dateFormat;
14013 cv = function(v){ return v; };
14016 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14020 return v !== undefined && v !== null && v !== '' ?
14021 parseInt(String(v).replace(stripRe, ""), 10) : '';
14026 return v !== undefined && v !== null && v !== '' ?
14027 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14032 cv = function(v){ return v === true || v === "true" || v == 1; };
14039 if(v instanceof Date){
14043 if(dateFormat == "timestamp"){
14044 return new Date(v*1000);
14046 return Date.parseDate(v, dateFormat);
14048 var parsed = Date.parse(v);
14049 return parsed ? new Date(parsed) : null;
14058 Roo.data.Field.prototype = {
14066 * Ext JS Library 1.1.1
14067 * Copyright(c) 2006-2007, Ext JS, LLC.
14069 * Originally Released Under LGPL - original licence link has changed is not relivant.
14072 * <script type="text/javascript">
14075 // Base class for reading structured data from a data source. This class is intended to be
14076 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14079 * @class Roo.data.DataReader
14080 * Base class for reading structured data from a data source. This class is intended to be
14081 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14084 Roo.data.DataReader = function(meta, recordType){
14088 this.recordType = recordType instanceof Array ?
14089 Roo.data.Record.create(recordType) : recordType;
14092 Roo.data.DataReader.prototype = {
14095 readerType : 'Data',
14097 * Create an empty record
14098 * @param {Object} data (optional) - overlay some values
14099 * @return {Roo.data.Record} record created.
14101 newRow : function(d) {
14103 this.recordType.prototype.fields.each(function(c) {
14105 case 'int' : da[c.name] = 0; break;
14106 case 'date' : da[c.name] = new Date(); break;
14107 case 'float' : da[c.name] = 0.0; break;
14108 case 'boolean' : da[c.name] = false; break;
14109 default : da[c.name] = ""; break;
14113 return new this.recordType(Roo.apply(da, d));
14119 * Ext JS Library 1.1.1
14120 * Copyright(c) 2006-2007, Ext JS, LLC.
14122 * Originally Released Under LGPL - original licence link has changed is not relivant.
14125 * <script type="text/javascript">
14129 * @class Roo.data.DataProxy
14130 * @extends Roo.data.Observable
14131 * This class is an abstract base class for implementations which provide retrieval of
14132 * unformatted data objects.<br>
14134 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14135 * (of the appropriate type which knows how to parse the data object) to provide a block of
14136 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14138 * Custom implementations must implement the load method as described in
14139 * {@link Roo.data.HttpProxy#load}.
14141 Roo.data.DataProxy = function(){
14144 * @event beforeload
14145 * Fires before a network request is made to retrieve a data object.
14146 * @param {Object} This DataProxy object.
14147 * @param {Object} params The params parameter to the load function.
14152 * Fires before the load method's callback is called.
14153 * @param {Object} This DataProxy object.
14154 * @param {Object} o The data object.
14155 * @param {Object} arg The callback argument object passed to the load function.
14159 * @event loadexception
14160 * Fires if an Exception occurs during data retrieval.
14161 * @param {Object} This DataProxy object.
14162 * @param {Object} o The data object.
14163 * @param {Object} arg The callback argument object passed to the load function.
14164 * @param {Object} e The Exception.
14166 loadexception : true
14168 Roo.data.DataProxy.superclass.constructor.call(this);
14171 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14174 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14178 * Ext JS Library 1.1.1
14179 * Copyright(c) 2006-2007, Ext JS, LLC.
14181 * Originally Released Under LGPL - original licence link has changed is not relivant.
14184 * <script type="text/javascript">
14187 * @class Roo.data.MemoryProxy
14188 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14189 * to the Reader when its load method is called.
14191 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14193 Roo.data.MemoryProxy = function(data){
14197 Roo.data.MemoryProxy.superclass.constructor.call(this);
14201 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14204 * Load data from the requested source (in this case an in-memory
14205 * data object passed to the constructor), read the data object into
14206 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14207 * process that block using the passed callback.
14208 * @param {Object} params This parameter is not used by the MemoryProxy class.
14209 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14210 * object into a block of Roo.data.Records.
14211 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14212 * The function must be passed <ul>
14213 * <li>The Record block object</li>
14214 * <li>The "arg" argument from the load function</li>
14215 * <li>A boolean success indicator</li>
14217 * @param {Object} scope The scope in which to call the callback
14218 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14220 load : function(params, reader, callback, scope, arg){
14221 params = params || {};
14224 result = reader.readRecords(params.data ? params.data :this.data);
14226 this.fireEvent("loadexception", this, arg, null, e);
14227 callback.call(scope, null, arg, false);
14230 callback.call(scope, result, arg, true);
14234 update : function(params, records){
14239 * Ext JS Library 1.1.1
14240 * Copyright(c) 2006-2007, Ext JS, LLC.
14242 * Originally Released Under LGPL - original licence link has changed is not relivant.
14245 * <script type="text/javascript">
14248 * @class Roo.data.HttpProxy
14249 * @extends Roo.data.DataProxy
14250 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14251 * configured to reference a certain URL.<br><br>
14253 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14254 * from which the running page was served.<br><br>
14256 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14258 * Be aware that to enable the browser to parse an XML document, the server must set
14259 * the Content-Type header in the HTTP response to "text/xml".
14261 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14262 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14263 * will be used to make the request.
14265 Roo.data.HttpProxy = function(conn){
14266 Roo.data.HttpProxy.superclass.constructor.call(this);
14267 // is conn a conn config or a real conn?
14269 this.useAjax = !conn || !conn.events;
14273 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14274 // thse are take from connection...
14277 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14280 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14281 * extra parameters to each request made by this object. (defaults to undefined)
14284 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14285 * to each request made by this object. (defaults to undefined)
14288 * @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)
14291 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14294 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14300 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14304 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14305 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14306 * a finer-grained basis than the DataProxy events.
14308 getConnection : function(){
14309 return this.useAjax ? Roo.Ajax : this.conn;
14313 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14314 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14315 * process that block using the passed callback.
14316 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14317 * for the request to the remote server.
14318 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14319 * object into a block of Roo.data.Records.
14320 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14321 * The function must be passed <ul>
14322 * <li>The Record block object</li>
14323 * <li>The "arg" argument from the load function</li>
14324 * <li>A boolean success indicator</li>
14326 * @param {Object} scope The scope in which to call the callback
14327 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14329 load : function(params, reader, callback, scope, arg){
14330 if(this.fireEvent("beforeload", this, params) !== false){
14332 params : params || {},
14334 callback : callback,
14339 callback : this.loadResponse,
14343 Roo.applyIf(o, this.conn);
14344 if(this.activeRequest){
14345 Roo.Ajax.abort(this.activeRequest);
14347 this.activeRequest = Roo.Ajax.request(o);
14349 this.conn.request(o);
14352 callback.call(scope||this, null, arg, false);
14357 loadResponse : function(o, success, response){
14358 delete this.activeRequest;
14360 this.fireEvent("loadexception", this, o, response);
14361 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14366 result = o.reader.read(response);
14368 this.fireEvent("loadexception", this, o, response, e);
14369 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14373 this.fireEvent("load", this, o, o.request.arg);
14374 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14378 update : function(dataSet){
14383 updateResponse : function(dataSet){
14388 * Ext JS Library 1.1.1
14389 * Copyright(c) 2006-2007, Ext JS, LLC.
14391 * Originally Released Under LGPL - original licence link has changed is not relivant.
14394 * <script type="text/javascript">
14398 * @class Roo.data.ScriptTagProxy
14399 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14400 * other than the originating domain of the running page.<br><br>
14402 * <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
14403 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14405 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14406 * source code that is used as the source inside a <script> tag.<br><br>
14408 * In order for the browser to process the returned data, the server must wrap the data object
14409 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14410 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14411 * depending on whether the callback name was passed:
14414 boolean scriptTag = false;
14415 String cb = request.getParameter("callback");
14418 response.setContentType("text/javascript");
14420 response.setContentType("application/x-json");
14422 Writer out = response.getWriter();
14424 out.write(cb + "(");
14426 out.print(dataBlock.toJsonString());
14433 * @param {Object} config A configuration object.
14435 Roo.data.ScriptTagProxy = function(config){
14436 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14437 Roo.apply(this, config);
14438 this.head = document.getElementsByTagName("head")[0];
14441 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14443 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14445 * @cfg {String} url The URL from which to request the data object.
14448 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14452 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14453 * the server the name of the callback function set up by the load call to process the returned data object.
14454 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14455 * javascript output which calls this named function passing the data object as its only parameter.
14457 callbackParam : "callback",
14459 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14460 * name to the request.
14465 * Load data from the configured URL, read the data object into
14466 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14467 * process that block using the passed callback.
14468 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14469 * for the request to the remote server.
14470 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14471 * object into a block of Roo.data.Records.
14472 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14473 * The function must be passed <ul>
14474 * <li>The Record block object</li>
14475 * <li>The "arg" argument from the load function</li>
14476 * <li>A boolean success indicator</li>
14478 * @param {Object} scope The scope in which to call the callback
14479 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14481 load : function(params, reader, callback, scope, arg){
14482 if(this.fireEvent("beforeload", this, params) !== false){
14484 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14486 var url = this.url;
14487 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14489 url += "&_dc=" + (new Date().getTime());
14491 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14494 cb : "stcCallback"+transId,
14495 scriptId : "stcScript"+transId,
14499 callback : callback,
14505 window[trans.cb] = function(o){
14506 conn.handleResponse(o, trans);
14509 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14511 if(this.autoAbort !== false){
14515 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14517 var script = document.createElement("script");
14518 script.setAttribute("src", url);
14519 script.setAttribute("type", "text/javascript");
14520 script.setAttribute("id", trans.scriptId);
14521 this.head.appendChild(script);
14523 this.trans = trans;
14525 callback.call(scope||this, null, arg, false);
14530 isLoading : function(){
14531 return this.trans ? true : false;
14535 * Abort the current server request.
14537 abort : function(){
14538 if(this.isLoading()){
14539 this.destroyTrans(this.trans);
14544 destroyTrans : function(trans, isLoaded){
14545 this.head.removeChild(document.getElementById(trans.scriptId));
14546 clearTimeout(trans.timeoutId);
14548 window[trans.cb] = undefined;
14550 delete window[trans.cb];
14553 // if hasn't been loaded, wait for load to remove it to prevent script error
14554 window[trans.cb] = function(){
14555 window[trans.cb] = undefined;
14557 delete window[trans.cb];
14564 handleResponse : function(o, trans){
14565 this.trans = false;
14566 this.destroyTrans(trans, true);
14569 result = trans.reader.readRecords(o);
14571 this.fireEvent("loadexception", this, o, trans.arg, e);
14572 trans.callback.call(trans.scope||window, null, trans.arg, false);
14575 this.fireEvent("load", this, o, trans.arg);
14576 trans.callback.call(trans.scope||window, result, trans.arg, true);
14580 handleFailure : function(trans){
14581 this.trans = false;
14582 this.destroyTrans(trans, false);
14583 this.fireEvent("loadexception", this, null, trans.arg);
14584 trans.callback.call(trans.scope||window, null, trans.arg, false);
14588 * Ext JS Library 1.1.1
14589 * Copyright(c) 2006-2007, Ext JS, LLC.
14591 * Originally Released Under LGPL - original licence link has changed is not relivant.
14594 * <script type="text/javascript">
14598 * @class Roo.data.JsonReader
14599 * @extends Roo.data.DataReader
14600 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14601 * based on mappings in a provided Roo.data.Record constructor.
14603 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14604 * in the reply previously.
14609 var RecordDef = Roo.data.Record.create([
14610 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14611 {name: 'occupation'} // This field will use "occupation" as the mapping.
14613 var myReader = new Roo.data.JsonReader({
14614 totalProperty: "results", // The property which contains the total dataset size (optional)
14615 root: "rows", // The property which contains an Array of row objects
14616 id: "id" // The property within each row object that provides an ID for the record (optional)
14620 * This would consume a JSON file like this:
14622 { 'results': 2, 'rows': [
14623 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14624 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14627 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14628 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14629 * paged from the remote server.
14630 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14631 * @cfg {String} root name of the property which contains the Array of row objects.
14632 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14633 * @cfg {Array} fields Array of field definition objects
14635 * Create a new JsonReader
14636 * @param {Object} meta Metadata configuration options
14637 * @param {Object} recordType Either an Array of field definition objects,
14638 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14640 Roo.data.JsonReader = function(meta, recordType){
14643 // set some defaults:
14644 Roo.applyIf(meta, {
14645 totalProperty: 'total',
14646 successProperty : 'success',
14651 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14653 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14655 readerType : 'Json',
14658 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14659 * Used by Store query builder to append _requestMeta to params.
14662 metaFromRemote : false,
14664 * This method is only used by a DataProxy which has retrieved data from a remote server.
14665 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14666 * @return {Object} data A data block which is used by an Roo.data.Store object as
14667 * a cache of Roo.data.Records.
14669 read : function(response){
14670 var json = response.responseText;
14672 var o = /* eval:var:o */ eval("("+json+")");
14674 throw {message: "JsonReader.read: Json object not found"};
14680 this.metaFromRemote = true;
14681 this.meta = o.metaData;
14682 this.recordType = Roo.data.Record.create(o.metaData.fields);
14683 this.onMetaChange(this.meta, this.recordType, o);
14685 return this.readRecords(o);
14688 // private function a store will implement
14689 onMetaChange : function(meta, recordType, o){
14696 simpleAccess: function(obj, subsc) {
14703 getJsonAccessor: function(){
14705 return function(expr) {
14707 return(re.test(expr))
14708 ? new Function("obj", "return obj." + expr)
14713 return Roo.emptyFn;
14718 * Create a data block containing Roo.data.Records from an XML document.
14719 * @param {Object} o An object which contains an Array of row objects in the property specified
14720 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14721 * which contains the total size of the dataset.
14722 * @return {Object} data A data block which is used by an Roo.data.Store object as
14723 * a cache of Roo.data.Records.
14725 readRecords : function(o){
14727 * After any data loads, the raw JSON data is available for further custom processing.
14731 var s = this.meta, Record = this.recordType,
14732 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14734 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14736 if(s.totalProperty) {
14737 this.getTotal = this.getJsonAccessor(s.totalProperty);
14739 if(s.successProperty) {
14740 this.getSuccess = this.getJsonAccessor(s.successProperty);
14742 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14744 var g = this.getJsonAccessor(s.id);
14745 this.getId = function(rec) {
14747 return (r === undefined || r === "") ? null : r;
14750 this.getId = function(){return null;};
14753 for(var jj = 0; jj < fl; jj++){
14755 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14756 this.ef[jj] = this.getJsonAccessor(map);
14760 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14761 if(s.totalProperty){
14762 var vt = parseInt(this.getTotal(o), 10);
14767 if(s.successProperty){
14768 var vs = this.getSuccess(o);
14769 if(vs === false || vs === 'false'){
14774 for(var i = 0; i < c; i++){
14777 var id = this.getId(n);
14778 for(var j = 0; j < fl; j++){
14780 var v = this.ef[j](n);
14782 Roo.log('missing convert for ' + f.name);
14786 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14788 var record = new Record(values, id);
14790 records[i] = record;
14796 totalRecords : totalRecords
14799 // used when loading children.. @see loadDataFromChildren
14800 toLoadData: function(rec)
14802 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14803 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14804 return { data : data, total : data.length };
14809 * Ext JS Library 1.1.1
14810 * Copyright(c) 2006-2007, Ext JS, LLC.
14812 * Originally Released Under LGPL - original licence link has changed is not relivant.
14815 * <script type="text/javascript">
14819 * @class Roo.data.ArrayReader
14820 * @extends Roo.data.DataReader
14821 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14822 * Each element of that Array represents a row of data fields. The
14823 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14824 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14828 var RecordDef = Roo.data.Record.create([
14829 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14830 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14832 var myReader = new Roo.data.ArrayReader({
14833 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14837 * This would consume an Array like this:
14839 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14843 * Create a new JsonReader
14844 * @param {Object} meta Metadata configuration options.
14845 * @param {Object|Array} recordType Either an Array of field definition objects
14847 * @cfg {Array} fields Array of field definition objects
14848 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14849 * as specified to {@link Roo.data.Record#create},
14850 * or an {@link Roo.data.Record} object
14853 * created using {@link Roo.data.Record#create}.
14855 Roo.data.ArrayReader = function(meta, recordType)
14857 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14860 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14863 * Create a data block containing Roo.data.Records from an XML document.
14864 * @param {Object} o An Array of row objects which represents the dataset.
14865 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14866 * a cache of Roo.data.Records.
14868 readRecords : function(o)
14870 var sid = this.meta ? this.meta.id : null;
14871 var recordType = this.recordType, fields = recordType.prototype.fields;
14874 for(var i = 0; i < root.length; i++){
14877 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14878 for(var j = 0, jlen = fields.length; j < jlen; j++){
14879 var f = fields.items[j];
14880 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14881 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14883 values[f.name] = v;
14885 var record = new recordType(values, id);
14887 records[records.length] = record;
14891 totalRecords : records.length
14894 // used when loading children.. @see loadDataFromChildren
14895 toLoadData: function(rec)
14897 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14898 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14909 * @class Roo.bootstrap.ComboBox
14910 * @extends Roo.bootstrap.TriggerField
14911 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14912 * @cfg {Boolean} append (true|false) default false
14913 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14914 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14915 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14916 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14917 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14918 * @cfg {Boolean} animate default true
14919 * @cfg {Boolean} emptyResultText only for touch device
14920 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14921 * @cfg {String} emptyTitle default ''
14922 * @cfg {Number} width fixed with? experimental
14924 * Create a new ComboBox.
14925 * @param {Object} config Configuration options
14927 Roo.bootstrap.ComboBox = function(config){
14928 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14932 * Fires when the dropdown list is expanded
14933 * @param {Roo.bootstrap.ComboBox} combo This combo box
14938 * Fires when the dropdown list is collapsed
14939 * @param {Roo.bootstrap.ComboBox} combo This combo box
14943 * @event beforeselect
14944 * Fires before a list item is selected. Return false to cancel the selection.
14945 * @param {Roo.bootstrap.ComboBox} combo This combo box
14946 * @param {Roo.data.Record} record The data record returned from the underlying store
14947 * @param {Number} index The index of the selected item in the dropdown list
14949 'beforeselect' : true,
14952 * Fires when a list item is selected
14953 * @param {Roo.bootstrap.ComboBox} combo This combo box
14954 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14955 * @param {Number} index The index of the selected item in the dropdown list
14959 * @event beforequery
14960 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14961 * The event object passed has these properties:
14962 * @param {Roo.bootstrap.ComboBox} combo This combo box
14963 * @param {String} query The query
14964 * @param {Boolean} forceAll true to force "all" query
14965 * @param {Boolean} cancel true to cancel the query
14966 * @param {Object} e The query event object
14968 'beforequery': true,
14971 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14972 * @param {Roo.bootstrap.ComboBox} combo This combo box
14977 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14978 * @param {Roo.bootstrap.ComboBox} combo This combo box
14979 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14984 * Fires when the remove value from the combobox array
14985 * @param {Roo.bootstrap.ComboBox} combo This combo box
14989 * @event afterremove
14990 * Fires when the remove value from the combobox array
14991 * @param {Roo.bootstrap.ComboBox} combo This combo box
14993 'afterremove' : true,
14995 * @event specialfilter
14996 * Fires when specialfilter
14997 * @param {Roo.bootstrap.ComboBox} combo This combo box
14999 'specialfilter' : true,
15002 * Fires when tick the element
15003 * @param {Roo.bootstrap.ComboBox} combo This combo box
15007 * @event touchviewdisplay
15008 * Fires when touch view require special display (default is using displayField)
15009 * @param {Roo.bootstrap.ComboBox} combo This combo box
15010 * @param {Object} cfg set html .
15012 'touchviewdisplay' : true
15017 this.tickItems = [];
15019 this.selectedIndex = -1;
15020 if(this.mode == 'local'){
15021 if(config.queryDelay === undefined){
15022 this.queryDelay = 10;
15024 if(config.minChars === undefined){
15030 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15033 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15034 * rendering into an Roo.Editor, defaults to false)
15037 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15038 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15041 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15044 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15045 * the dropdown list (defaults to undefined, with no header element)
15049 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15053 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15055 listWidth: undefined,
15057 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15058 * mode = 'remote' or 'text' if mode = 'local')
15060 displayField: undefined,
15063 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15064 * mode = 'remote' or 'value' if mode = 'local').
15065 * Note: use of a valueField requires the user make a selection
15066 * in order for a value to be mapped.
15068 valueField: undefined,
15070 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15075 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15076 * field's data value (defaults to the underlying DOM element's name)
15078 hiddenName: undefined,
15080 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15084 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15086 selectedClass: 'active',
15089 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15093 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15094 * anchor positions (defaults to 'tl-bl')
15096 listAlign: 'tl-bl?',
15098 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15102 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15103 * query specified by the allQuery config option (defaults to 'query')
15105 triggerAction: 'query',
15107 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15108 * (defaults to 4, does not apply if editable = false)
15112 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15113 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15117 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15118 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15122 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15123 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15127 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15128 * when editable = true (defaults to false)
15130 selectOnFocus:false,
15132 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15134 queryParam: 'query',
15136 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15137 * when mode = 'remote' (defaults to 'Loading...')
15139 loadingText: 'Loading...',
15141 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15145 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15149 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15150 * traditional select (defaults to true)
15154 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15158 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15162 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15163 * listWidth has a higher value)
15167 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15168 * allow the user to set arbitrary text into the field (defaults to false)
15170 forceSelection:false,
15172 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15173 * if typeAhead = true (defaults to 250)
15175 typeAheadDelay : 250,
15177 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15178 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15180 valueNotFoundText : undefined,
15182 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15184 blockFocus : false,
15187 * @cfg {Boolean} disableClear Disable showing of clear button.
15189 disableClear : false,
15191 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15193 alwaysQuery : false,
15196 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15201 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15203 invalidClass : "has-warning",
15206 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15208 validClass : "has-success",
15211 * @cfg {Boolean} specialFilter (true|false) special filter default false
15213 specialFilter : false,
15216 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15218 mobileTouchView : true,
15221 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15223 useNativeIOS : false,
15226 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15228 mobile_restrict_height : false,
15230 ios_options : false,
15242 btnPosition : 'right',
15243 triggerList : true,
15244 showToggleBtn : true,
15246 emptyResultText: 'Empty',
15247 triggerText : 'Select',
15251 // element that contains real text value.. (when hidden is used..)
15253 getAutoCreate : function()
15258 * Render classic select for iso
15261 if(Roo.isIOS && this.useNativeIOS){
15262 cfg = this.getAutoCreateNativeIOS();
15270 if(Roo.isTouch && this.mobileTouchView){
15271 cfg = this.getAutoCreateTouchView();
15278 if(!this.tickable){
15279 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15284 * ComboBox with tickable selections
15287 var align = this.labelAlign || this.parentLabelAlign();
15290 cls : 'form-group roo-combobox-tickable' //input-group
15293 var btn_text_select = '';
15294 var btn_text_done = '';
15295 var btn_text_cancel = '';
15297 if (this.btn_text_show) {
15298 btn_text_select = 'Select';
15299 btn_text_done = 'Done';
15300 btn_text_cancel = 'Cancel';
15305 cls : 'tickable-buttons',
15310 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15311 //html : this.triggerText
15312 html: btn_text_select
15318 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15320 html: btn_text_done
15326 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15328 html: btn_text_cancel
15334 buttons.cn.unshift({
15336 cls: 'roo-select2-search-field-input'
15342 Roo.each(buttons.cn, function(c){
15344 c.cls += ' btn-' + _this.size;
15347 if (_this.disabled) {
15354 style : 'display: contents',
15359 cls: 'form-hidden-field'
15363 cls: 'roo-select2-choices',
15367 cls: 'roo-select2-search-field',
15378 cls: 'roo-select2-container input-group roo-select2-container-multi',
15384 // cls: 'typeahead typeahead-long dropdown-menu',
15385 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15390 if(this.hasFeedback && !this.allowBlank){
15394 cls: 'glyphicon form-control-feedback'
15397 combobox.cn.push(feedback);
15404 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15405 tooltip : 'This field is required'
15407 if (Roo.bootstrap.version == 4) {
15410 style : 'display:none'
15413 if (align ==='left' && this.fieldLabel.length) {
15415 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15422 cls : 'control-label col-form-label',
15423 html : this.fieldLabel
15435 var labelCfg = cfg.cn[1];
15436 var contentCfg = cfg.cn[2];
15439 if(this.indicatorpos == 'right'){
15445 cls : 'control-label col-form-label',
15449 html : this.fieldLabel
15465 labelCfg = cfg.cn[0];
15466 contentCfg = cfg.cn[1];
15470 if(this.labelWidth > 12){
15471 labelCfg.style = "width: " + this.labelWidth + 'px';
15473 if(this.width * 1 > 0){
15474 contentCfg.style = "width: " + this.width + 'px';
15476 if(this.labelWidth < 13 && this.labelmd == 0){
15477 this.labelmd = this.labelWidth;
15480 if(this.labellg > 0){
15481 labelCfg.cls += ' col-lg-' + this.labellg;
15482 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15485 if(this.labelmd > 0){
15486 labelCfg.cls += ' col-md-' + this.labelmd;
15487 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15490 if(this.labelsm > 0){
15491 labelCfg.cls += ' col-sm-' + this.labelsm;
15492 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15495 if(this.labelxs > 0){
15496 labelCfg.cls += ' col-xs-' + this.labelxs;
15497 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15501 } else if ( this.fieldLabel.length) {
15502 // Roo.log(" label");
15507 //cls : 'input-group-addon',
15508 html : this.fieldLabel
15513 if(this.indicatorpos == 'right'){
15517 //cls : 'input-group-addon',
15518 html : this.fieldLabel
15528 // Roo.log(" no label && no align");
15535 ['xs','sm','md','lg'].map(function(size){
15536 if (settings[size]) {
15537 cfg.cls += ' col-' + size + '-' + settings[size];
15545 _initEventsCalled : false,
15548 initEvents: function()
15550 if (this._initEventsCalled) { // as we call render... prevent looping...
15553 this._initEventsCalled = true;
15556 throw "can not find store for combo";
15559 this.indicator = this.indicatorEl();
15561 this.store = Roo.factory(this.store, Roo.data);
15562 this.store.parent = this;
15564 // if we are building from html. then this element is so complex, that we can not really
15565 // use the rendered HTML.
15566 // so we have to trash and replace the previous code.
15567 if (Roo.XComponent.build_from_html) {
15568 // remove this element....
15569 var e = this.el.dom, k=0;
15570 while (e ) { e = e.previousSibling; ++k;}
15575 this.rendered = false;
15577 this.render(this.parent().getChildContainer(true), k);
15580 if(Roo.isIOS && this.useNativeIOS){
15581 this.initIOSView();
15589 if(Roo.isTouch && this.mobileTouchView){
15590 this.initTouchView();
15595 this.initTickableEvents();
15599 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15601 if(this.hiddenName){
15603 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15605 this.hiddenField.dom.value =
15606 this.hiddenValue !== undefined ? this.hiddenValue :
15607 this.value !== undefined ? this.value : '';
15609 // prevent input submission
15610 this.el.dom.removeAttribute('name');
15611 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15616 // this.el.dom.setAttribute('autocomplete', 'off');
15619 var cls = 'x-combo-list';
15621 //this.list = new Roo.Layer({
15622 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15628 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15629 _this.list.setWidth(lw);
15632 this.list.on('mouseover', this.onViewOver, this);
15633 this.list.on('mousemove', this.onViewMove, this);
15634 this.list.on('scroll', this.onViewScroll, this);
15637 this.list.swallowEvent('mousewheel');
15638 this.assetHeight = 0;
15641 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15642 this.assetHeight += this.header.getHeight();
15645 this.innerList = this.list.createChild({cls:cls+'-inner'});
15646 this.innerList.on('mouseover', this.onViewOver, this);
15647 this.innerList.on('mousemove', this.onViewMove, this);
15648 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15650 if(this.allowBlank && !this.pageSize && !this.disableClear){
15651 this.footer = this.list.createChild({cls:cls+'-ft'});
15652 this.pageTb = new Roo.Toolbar(this.footer);
15656 this.footer = this.list.createChild({cls:cls+'-ft'});
15657 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15658 {pageSize: this.pageSize});
15662 if (this.pageTb && this.allowBlank && !this.disableClear) {
15664 this.pageTb.add(new Roo.Toolbar.Fill(), {
15665 cls: 'x-btn-icon x-btn-clear',
15667 handler: function()
15670 _this.clearValue();
15671 _this.onSelect(false, -1);
15676 this.assetHeight += this.footer.getHeight();
15681 this.tpl = Roo.bootstrap.version == 4 ?
15682 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15683 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15686 this.view = new Roo.View(this.list, this.tpl, {
15687 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15689 //this.view.wrapEl.setDisplayed(false);
15690 this.view.on('click', this.onViewClick, this);
15693 this.store.on('beforeload', this.onBeforeLoad, this);
15694 this.store.on('load', this.onLoad, this);
15695 this.store.on('loadexception', this.onLoadException, this);
15697 if(this.resizable){
15698 this.resizer = new Roo.Resizable(this.list, {
15699 pinned:true, handles:'se'
15701 this.resizer.on('resize', function(r, w, h){
15702 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15703 this.listWidth = w;
15704 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15705 this.restrictHeight();
15707 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15710 if(!this.editable){
15711 this.editable = true;
15712 this.setEditable(false);
15717 if (typeof(this.events.add.listeners) != 'undefined') {
15719 this.addicon = this.wrap.createChild(
15720 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15722 this.addicon.on('click', function(e) {
15723 this.fireEvent('add', this);
15726 if (typeof(this.events.edit.listeners) != 'undefined') {
15728 this.editicon = this.wrap.createChild(
15729 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15730 if (this.addicon) {
15731 this.editicon.setStyle('margin-left', '40px');
15733 this.editicon.on('click', function(e) {
15735 // we fire even if inothing is selected..
15736 this.fireEvent('edit', this, this.lastData );
15742 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15743 "up" : function(e){
15744 this.inKeyMode = true;
15748 "down" : function(e){
15749 if(!this.isExpanded()){
15750 this.onTriggerClick();
15752 this.inKeyMode = true;
15757 "enter" : function(e){
15758 // this.onViewClick();
15762 if(this.fireEvent("specialkey", this, e)){
15763 this.onViewClick(false);
15769 "esc" : function(e){
15773 "tab" : function(e){
15776 if(this.fireEvent("specialkey", this, e)){
15777 this.onViewClick(false);
15785 doRelay : function(foo, bar, hname){
15786 if(hname == 'down' || this.scope.isExpanded()){
15787 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15796 this.queryDelay = Math.max(this.queryDelay || 10,
15797 this.mode == 'local' ? 10 : 250);
15800 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15802 if(this.typeAhead){
15803 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15805 if(this.editable !== false){
15806 this.inputEl().on("keyup", this.onKeyUp, this);
15808 if(this.forceSelection){
15809 this.inputEl().on('blur', this.doForce, this);
15813 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15814 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15818 initTickableEvents: function()
15822 if(this.hiddenName){
15824 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15826 this.hiddenField.dom.value =
15827 this.hiddenValue !== undefined ? this.hiddenValue :
15828 this.value !== undefined ? this.value : '';
15830 // prevent input submission
15831 this.el.dom.removeAttribute('name');
15832 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15837 // this.list = this.el.select('ul.dropdown-menu',true).first();
15839 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15840 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15841 if(this.triggerList){
15842 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15845 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15846 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15848 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15849 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15851 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15852 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15854 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15855 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15856 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15859 this.cancelBtn.hide();
15864 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15865 _this.list.setWidth(lw);
15868 this.list.on('mouseover', this.onViewOver, this);
15869 this.list.on('mousemove', this.onViewMove, this);
15871 this.list.on('scroll', this.onViewScroll, this);
15874 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15875 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15878 this.view = new Roo.View(this.list, this.tpl, {
15883 selectedClass: this.selectedClass
15886 //this.view.wrapEl.setDisplayed(false);
15887 this.view.on('click', this.onViewClick, this);
15891 this.store.on('beforeload', this.onBeforeLoad, this);
15892 this.store.on('load', this.onLoad, this);
15893 this.store.on('loadexception', this.onLoadException, this);
15896 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15897 "up" : function(e){
15898 this.inKeyMode = true;
15902 "down" : function(e){
15903 this.inKeyMode = true;
15907 "enter" : function(e){
15908 if(this.fireEvent("specialkey", this, e)){
15909 this.onViewClick(false);
15915 "esc" : function(e){
15916 this.onTickableFooterButtonClick(e, false, false);
15919 "tab" : function(e){
15920 this.fireEvent("specialkey", this, e);
15922 this.onTickableFooterButtonClick(e, false, false);
15929 doRelay : function(e, fn, key){
15930 if(this.scope.isExpanded()){
15931 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15940 this.queryDelay = Math.max(this.queryDelay || 10,
15941 this.mode == 'local' ? 10 : 250);
15944 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15946 if(this.typeAhead){
15947 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15950 if(this.editable !== false){
15951 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15954 this.indicator = this.indicatorEl();
15956 if(this.indicator){
15957 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15958 this.indicator.hide();
15963 onDestroy : function(){
15965 this.view.setStore(null);
15966 this.view.el.removeAllListeners();
15967 this.view.el.remove();
15968 this.view.purgeListeners();
15971 this.list.dom.innerHTML = '';
15975 this.store.un('beforeload', this.onBeforeLoad, this);
15976 this.store.un('load', this.onLoad, this);
15977 this.store.un('loadexception', this.onLoadException, this);
15979 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15983 fireKey : function(e){
15984 if(e.isNavKeyPress() && !this.list.isVisible()){
15985 this.fireEvent("specialkey", this, e);
15990 onResize: function(w, h)
15994 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15996 // if(typeof w != 'number'){
15997 // // we do not handle it!?!?
16000 // var tw = this.trigger.getWidth();
16001 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16002 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16004 // this.inputEl().setWidth( this.adjustWidth('input', x));
16006 // //this.trigger.setStyle('left', x+'px');
16008 // if(this.list && this.listWidth === undefined){
16009 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16010 // this.list.setWidth(lw);
16011 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16019 * Allow or prevent the user from directly editing the field text. If false is passed,
16020 * the user will only be able to select from the items defined in the dropdown list. This method
16021 * is the runtime equivalent of setting the 'editable' config option at config time.
16022 * @param {Boolean} value True to allow the user to directly edit the field text
16024 setEditable : function(value){
16025 if(value == this.editable){
16028 this.editable = value;
16030 this.inputEl().dom.setAttribute('readOnly', true);
16031 this.inputEl().on('mousedown', this.onTriggerClick, this);
16032 this.inputEl().addClass('x-combo-noedit');
16034 this.inputEl().dom.setAttribute('readOnly', false);
16035 this.inputEl().un('mousedown', this.onTriggerClick, this);
16036 this.inputEl().removeClass('x-combo-noedit');
16042 onBeforeLoad : function(combo,opts){
16043 if(!this.hasFocus){
16047 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16049 this.restrictHeight();
16050 this.selectedIndex = -1;
16054 onLoad : function(){
16056 this.hasQuery = false;
16058 if(!this.hasFocus){
16062 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16063 this.loading.hide();
16066 if(this.store.getCount() > 0){
16069 this.restrictHeight();
16070 if(this.lastQuery == this.allQuery){
16071 if(this.editable && !this.tickable){
16072 this.inputEl().dom.select();
16076 !this.selectByValue(this.value, true) &&
16079 !this.store.lastOptions ||
16080 typeof(this.store.lastOptions.add) == 'undefined' ||
16081 this.store.lastOptions.add != true
16084 this.select(0, true);
16087 if(this.autoFocus){
16090 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16091 this.taTask.delay(this.typeAheadDelay);
16095 this.onEmptyResults();
16101 onLoadException : function()
16103 this.hasQuery = false;
16105 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16106 this.loading.hide();
16109 if(this.tickable && this.editable){
16114 // only causes errors at present
16115 //Roo.log(this.store.reader.jsonData);
16116 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16118 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16124 onTypeAhead : function(){
16125 if(this.store.getCount() > 0){
16126 var r = this.store.getAt(0);
16127 var newValue = r.data[this.displayField];
16128 var len = newValue.length;
16129 var selStart = this.getRawValue().length;
16131 if(selStart != len){
16132 this.setRawValue(newValue);
16133 this.selectText(selStart, newValue.length);
16139 onSelect : function(record, index){
16141 if(this.fireEvent('beforeselect', this, record, index) !== false){
16143 this.setFromData(index > -1 ? record.data : false);
16146 this.fireEvent('select', this, record, index);
16151 * Returns the currently selected field value or empty string if no value is set.
16152 * @return {String} value The selected value
16154 getValue : function()
16156 if(Roo.isIOS && this.useNativeIOS){
16157 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16161 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16164 if(this.valueField){
16165 return typeof this.value != 'undefined' ? this.value : '';
16167 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16171 getRawValue : function()
16173 if(Roo.isIOS && this.useNativeIOS){
16174 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16177 var v = this.inputEl().getValue();
16183 * Clears any text/value currently set in the field
16185 clearValue : function(){
16187 if(this.hiddenField){
16188 this.hiddenField.dom.value = '';
16191 this.setRawValue('');
16192 this.lastSelectionText = '';
16193 this.lastData = false;
16195 var close = this.closeTriggerEl();
16206 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16207 * will be displayed in the field. If the value does not match the data value of an existing item,
16208 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16209 * Otherwise the field will be blank (although the value will still be set).
16210 * @param {String} value The value to match
16212 setValue : function(v)
16214 if(Roo.isIOS && this.useNativeIOS){
16215 this.setIOSValue(v);
16225 if(this.valueField){
16226 var r = this.findRecord(this.valueField, v);
16228 text = r.data[this.displayField];
16229 }else if(this.valueNotFoundText !== undefined){
16230 text = this.valueNotFoundText;
16233 this.lastSelectionText = text;
16234 if(this.hiddenField){
16235 this.hiddenField.dom.value = v;
16237 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16240 var close = this.closeTriggerEl();
16243 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16249 * @property {Object} the last set data for the element
16254 * Sets the value of the field based on a object which is related to the record format for the store.
16255 * @param {Object} value the value to set as. or false on reset?
16257 setFromData : function(o){
16264 var dv = ''; // display value
16265 var vv = ''; // value value..
16267 if (this.displayField) {
16268 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16270 // this is an error condition!!!
16271 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16274 if(this.valueField){
16275 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16278 var close = this.closeTriggerEl();
16281 if(dv.length || vv * 1 > 0){
16283 this.blockFocus=true;
16289 if(this.hiddenField){
16290 this.hiddenField.dom.value = vv;
16292 this.lastSelectionText = dv;
16293 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16297 // no hidden field.. - we store the value in 'value', but still display
16298 // display field!!!!
16299 this.lastSelectionText = dv;
16300 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16307 reset : function(){
16308 // overridden so that last data is reset..
16315 this.setValue(this.originalValue);
16316 //this.clearInvalid();
16317 this.lastData = false;
16319 this.view.clearSelections();
16325 findRecord : function(prop, value){
16327 if(this.store.getCount() > 0){
16328 this.store.each(function(r){
16329 if(r.data[prop] == value){
16339 getName: function()
16341 // returns hidden if it's set..
16342 if (!this.rendered) {return ''};
16343 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16347 onViewMove : function(e, t){
16348 this.inKeyMode = false;
16352 onViewOver : function(e, t){
16353 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16356 var item = this.view.findItemFromChild(t);
16359 var index = this.view.indexOf(item);
16360 this.select(index, false);
16365 onViewClick : function(view, doFocus, el, e)
16367 var index = this.view.getSelectedIndexes()[0];
16369 var r = this.store.getAt(index);
16373 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16380 Roo.each(this.tickItems, function(v,k){
16382 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16384 _this.tickItems.splice(k, 1);
16386 if(typeof(e) == 'undefined' && view == false){
16387 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16399 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16400 this.tickItems.push(r.data);
16403 if(typeof(e) == 'undefined' && view == false){
16404 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16411 this.onSelect(r, index);
16413 if(doFocus !== false && !this.blockFocus){
16414 this.inputEl().focus();
16419 restrictHeight : function(){
16420 //this.innerList.dom.style.height = '';
16421 //var inner = this.innerList.dom;
16422 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16423 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16424 //this.list.beginUpdate();
16425 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16426 this.list.alignTo(this.inputEl(), this.listAlign);
16427 this.list.alignTo(this.inputEl(), this.listAlign);
16428 //this.list.endUpdate();
16432 onEmptyResults : function(){
16434 if(this.tickable && this.editable){
16435 this.hasFocus = false;
16436 this.restrictHeight();
16444 * Returns true if the dropdown list is expanded, else false.
16446 isExpanded : function(){
16447 return this.list.isVisible();
16451 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16452 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453 * @param {String} value The data value of the item to select
16454 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455 * selected item if it is not currently in view (defaults to true)
16456 * @return {Boolean} True if the value matched an item in the list, else false
16458 selectByValue : function(v, scrollIntoView){
16459 if(v !== undefined && v !== null){
16460 var r = this.findRecord(this.valueField || this.displayField, v);
16462 this.select(this.store.indexOf(r), scrollIntoView);
16470 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16471 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16472 * @param {Number} index The zero-based index of the list item to select
16473 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16474 * selected item if it is not currently in view (defaults to true)
16476 select : function(index, scrollIntoView){
16477 this.selectedIndex = index;
16478 this.view.select(index);
16479 if(scrollIntoView !== false){
16480 var el = this.view.getNode(index);
16482 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16485 this.list.scrollChildIntoView(el, false);
16491 selectNext : function(){
16492 var ct = this.store.getCount();
16494 if(this.selectedIndex == -1){
16496 }else if(this.selectedIndex < ct-1){
16497 this.select(this.selectedIndex+1);
16503 selectPrev : function(){
16504 var ct = this.store.getCount();
16506 if(this.selectedIndex == -1){
16508 }else if(this.selectedIndex != 0){
16509 this.select(this.selectedIndex-1);
16515 onKeyUp : function(e){
16516 if(this.editable !== false && !e.isSpecialKey()){
16517 this.lastKey = e.getKey();
16518 this.dqTask.delay(this.queryDelay);
16523 validateBlur : function(){
16524 return !this.list || !this.list.isVisible();
16528 initQuery : function(){
16530 var v = this.getRawValue();
16532 if(this.tickable && this.editable){
16533 v = this.tickableInputEl().getValue();
16540 doForce : function(){
16541 if(this.inputEl().dom.value.length > 0){
16542 this.inputEl().dom.value =
16543 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16549 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16550 * query allowing the query action to be canceled if needed.
16551 * @param {String} query The SQL query to execute
16552 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16553 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16554 * saved in the current store (defaults to false)
16556 doQuery : function(q, forceAll){
16558 if(q === undefined || q === null){
16563 forceAll: forceAll,
16567 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16572 forceAll = qe.forceAll;
16573 if(forceAll === true || (q.length >= this.minChars)){
16575 this.hasQuery = true;
16577 if(this.lastQuery != q || this.alwaysQuery){
16578 this.lastQuery = q;
16579 if(this.mode == 'local'){
16580 this.selectedIndex = -1;
16582 this.store.clearFilter();
16585 if(this.specialFilter){
16586 this.fireEvent('specialfilter', this);
16591 this.store.filter(this.displayField, q);
16594 this.store.fireEvent("datachanged", this.store);
16601 this.store.baseParams[this.queryParam] = q;
16603 var options = {params : this.getParams(q)};
16606 options.add = true;
16607 options.params.start = this.page * this.pageSize;
16610 this.store.load(options);
16613 * this code will make the page width larger, at the beginning, the list not align correctly,
16614 * we should expand the list on onLoad
16615 * so command out it
16620 this.selectedIndex = -1;
16625 this.loadNext = false;
16629 getParams : function(q){
16631 //p[this.queryParam] = q;
16635 p.limit = this.pageSize;
16641 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16643 collapse : function(){
16644 if(!this.isExpanded()){
16650 this.hasFocus = false;
16654 this.cancelBtn.hide();
16655 this.trigger.show();
16658 this.tickableInputEl().dom.value = '';
16659 this.tickableInputEl().blur();
16664 Roo.get(document).un('mousedown', this.collapseIf, this);
16665 Roo.get(document).un('mousewheel', this.collapseIf, this);
16666 if (!this.editable) {
16667 Roo.get(document).un('keydown', this.listKeyPress, this);
16669 this.fireEvent('collapse', this);
16675 collapseIf : function(e){
16676 var in_combo = e.within(this.el);
16677 var in_list = e.within(this.list);
16678 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16680 if (in_combo || in_list || is_list) {
16681 //e.stopPropagation();
16686 this.onTickableFooterButtonClick(e, false, false);
16694 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16696 expand : function(){
16698 if(this.isExpanded() || !this.hasFocus){
16702 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16703 this.list.setWidth(lw);
16709 this.restrictHeight();
16713 this.tickItems = Roo.apply([], this.item);
16716 this.cancelBtn.show();
16717 this.trigger.hide();
16720 this.tickableInputEl().focus();
16725 Roo.get(document).on('mousedown', this.collapseIf, this);
16726 Roo.get(document).on('mousewheel', this.collapseIf, this);
16727 if (!this.editable) {
16728 Roo.get(document).on('keydown', this.listKeyPress, this);
16731 this.fireEvent('expand', this);
16735 // Implements the default empty TriggerField.onTriggerClick function
16736 onTriggerClick : function(e)
16738 Roo.log('trigger click');
16740 if(this.disabled || !this.triggerList){
16745 this.loadNext = false;
16747 if(this.isExpanded()){
16749 if (!this.blockFocus) {
16750 this.inputEl().focus();
16754 this.hasFocus = true;
16755 if(this.triggerAction == 'all') {
16756 this.doQuery(this.allQuery, true);
16758 this.doQuery(this.getRawValue());
16760 if (!this.blockFocus) {
16761 this.inputEl().focus();
16766 onTickableTriggerClick : function(e)
16773 this.loadNext = false;
16774 this.hasFocus = true;
16776 if(this.triggerAction == 'all') {
16777 this.doQuery(this.allQuery, true);
16779 this.doQuery(this.getRawValue());
16783 onSearchFieldClick : function(e)
16785 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16786 this.onTickableFooterButtonClick(e, false, false);
16790 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16795 this.loadNext = false;
16796 this.hasFocus = true;
16798 if(this.triggerAction == 'all') {
16799 this.doQuery(this.allQuery, true);
16801 this.doQuery(this.getRawValue());
16805 listKeyPress : function(e)
16807 //Roo.log('listkeypress');
16808 // scroll to first matching element based on key pres..
16809 if (e.isSpecialKey()) {
16812 var k = String.fromCharCode(e.getKey()).toUpperCase();
16815 var csel = this.view.getSelectedNodes();
16816 var cselitem = false;
16818 var ix = this.view.indexOf(csel[0]);
16819 cselitem = this.store.getAt(ix);
16820 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16826 this.store.each(function(v) {
16828 // start at existing selection.
16829 if (cselitem.id == v.id) {
16835 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16836 match = this.store.indexOf(v);
16842 if (match === false) {
16843 return true; // no more action?
16846 this.view.select(match);
16847 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16848 sn.scrollIntoView(sn.dom.parentNode, false);
16851 onViewScroll : function(e, t){
16853 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){
16857 this.hasQuery = true;
16859 this.loading = this.list.select('.loading', true).first();
16861 if(this.loading === null){
16862 this.list.createChild({
16864 cls: 'loading roo-select2-more-results roo-select2-active',
16865 html: 'Loading more results...'
16868 this.loading = this.list.select('.loading', true).first();
16870 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16872 this.loading.hide();
16875 this.loading.show();
16880 this.loadNext = true;
16882 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16887 addItem : function(o)
16889 var dv = ''; // display value
16891 if (this.displayField) {
16892 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16894 // this is an error condition!!!
16895 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16902 var choice = this.choices.createChild({
16904 cls: 'roo-select2-search-choice',
16913 cls: 'roo-select2-search-choice-close fa fa-times',
16918 }, this.searchField);
16920 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16922 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16930 this.inputEl().dom.value = '';
16935 onRemoveItem : function(e, _self, o)
16937 e.preventDefault();
16939 this.lastItem = Roo.apply([], this.item);
16941 var index = this.item.indexOf(o.data) * 1;
16944 Roo.log('not this item?!');
16948 this.item.splice(index, 1);
16953 this.fireEvent('remove', this, e);
16959 syncValue : function()
16961 if(!this.item.length){
16968 Roo.each(this.item, function(i){
16969 if(_this.valueField){
16970 value.push(i[_this.valueField]);
16977 this.value = value.join(',');
16979 if(this.hiddenField){
16980 this.hiddenField.dom.value = this.value;
16983 this.store.fireEvent("datachanged", this.store);
16988 clearItem : function()
16990 if(!this.multiple){
16996 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17004 if(this.tickable && !Roo.isTouch){
17005 this.view.refresh();
17009 inputEl: function ()
17011 if(Roo.isIOS && this.useNativeIOS){
17012 return this.el.select('select.roo-ios-select', true).first();
17015 if(Roo.isTouch && this.mobileTouchView){
17016 return this.el.select('input.form-control',true).first();
17020 return this.searchField;
17023 return this.el.select('input.form-control',true).first();
17026 onTickableFooterButtonClick : function(e, btn, el)
17028 e.preventDefault();
17030 this.lastItem = Roo.apply([], this.item);
17032 if(btn && btn.name == 'cancel'){
17033 this.tickItems = Roo.apply([], this.item);
17042 Roo.each(this.tickItems, function(o){
17050 validate : function()
17052 if(this.getVisibilityEl().hasClass('hidden')){
17056 var v = this.getRawValue();
17059 v = this.getValue();
17062 if(this.disabled || this.allowBlank || v.length){
17067 this.markInvalid();
17071 tickableInputEl : function()
17073 if(!this.tickable || !this.editable){
17074 return this.inputEl();
17077 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17081 getAutoCreateTouchView : function()
17086 cls: 'form-group' //input-group
17092 type : this.inputType,
17093 cls : 'form-control x-combo-noedit',
17094 autocomplete: 'new-password',
17095 placeholder : this.placeholder || '',
17100 input.name = this.name;
17104 input.cls += ' input-' + this.size;
17107 if (this.disabled) {
17108 input.disabled = true;
17112 cls : 'roo-combobox-wrap',
17119 inputblock.cls += ' input-group';
17121 inputblock.cn.unshift({
17123 cls : 'input-group-addon input-group-prepend input-group-text',
17128 if(this.removable && !this.multiple){
17129 inputblock.cls += ' roo-removable';
17131 inputblock.cn.push({
17134 cls : 'roo-combo-removable-btn close'
17138 if(this.hasFeedback && !this.allowBlank){
17140 inputblock.cls += ' has-feedback';
17142 inputblock.cn.push({
17144 cls: 'glyphicon form-control-feedback'
17151 inputblock.cls += (this.before) ? '' : ' input-group';
17153 inputblock.cn.push({
17155 cls : 'input-group-addon input-group-append input-group-text',
17161 var ibwrap = inputblock;
17166 cls: 'roo-select2-choices',
17170 cls: 'roo-select2-search-field',
17183 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17188 cls: 'form-hidden-field'
17194 if(!this.multiple && this.showToggleBtn){
17200 if (this.caret != false) {
17203 cls: 'fa fa-' + this.caret
17210 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17212 Roo.bootstrap.version == 3 ? caret : '',
17215 cls: 'combobox-clear',
17229 combobox.cls += ' roo-select2-container-multi';
17232 var align = this.labelAlign || this.parentLabelAlign();
17234 if (align ==='left' && this.fieldLabel.length) {
17239 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17240 tooltip : 'This field is required'
17244 cls : 'control-label col-form-label',
17245 html : this.fieldLabel
17249 cls : 'roo-combobox-wrap ',
17256 var labelCfg = cfg.cn[1];
17257 var contentCfg = cfg.cn[2];
17260 if(this.indicatorpos == 'right'){
17265 cls : 'control-label col-form-label',
17269 html : this.fieldLabel
17273 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17274 tooltip : 'This field is required'
17279 cls : "roo-combobox-wrap ",
17287 labelCfg = cfg.cn[0];
17288 contentCfg = cfg.cn[1];
17293 if(this.labelWidth > 12){
17294 labelCfg.style = "width: " + this.labelWidth + 'px';
17297 if(this.labelWidth < 13 && this.labelmd == 0){
17298 this.labelmd = this.labelWidth;
17301 if(this.labellg > 0){
17302 labelCfg.cls += ' col-lg-' + this.labellg;
17303 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17306 if(this.labelmd > 0){
17307 labelCfg.cls += ' col-md-' + this.labelmd;
17308 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17311 if(this.labelsm > 0){
17312 labelCfg.cls += ' col-sm-' + this.labelsm;
17313 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17316 if(this.labelxs > 0){
17317 labelCfg.cls += ' col-xs-' + this.labelxs;
17318 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17322 } else if ( this.fieldLabel.length) {
17326 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17327 tooltip : 'This field is required'
17331 cls : 'control-label',
17332 html : this.fieldLabel
17343 if(this.indicatorpos == 'right'){
17347 cls : 'control-label',
17348 html : this.fieldLabel,
17352 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17353 tooltip : 'This field is required'
17370 var settings = this;
17372 ['xs','sm','md','lg'].map(function(size){
17373 if (settings[size]) {
17374 cfg.cls += ' col-' + size + '-' + settings[size];
17381 initTouchView : function()
17383 this.renderTouchView();
17385 this.touchViewEl.on('scroll', function(){
17386 this.el.dom.scrollTop = 0;
17389 this.originalValue = this.getValue();
17391 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17393 this.inputEl().on("click", this.showTouchView, this);
17394 if (this.triggerEl) {
17395 this.triggerEl.on("click", this.showTouchView, this);
17399 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17400 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17402 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17404 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17405 this.store.on('load', this.onTouchViewLoad, this);
17406 this.store.on('loadexception', this.onTouchViewLoadException, this);
17408 if(this.hiddenName){
17410 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17412 this.hiddenField.dom.value =
17413 this.hiddenValue !== undefined ? this.hiddenValue :
17414 this.value !== undefined ? this.value : '';
17416 this.el.dom.removeAttribute('name');
17417 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17421 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17422 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17425 if(this.removable && !this.multiple){
17426 var close = this.closeTriggerEl();
17428 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17429 close.on('click', this.removeBtnClick, this, close);
17433 * fix the bug in Safari iOS8
17435 this.inputEl().on("focus", function(e){
17436 document.activeElement.blur();
17439 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17446 renderTouchView : function()
17448 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17449 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17451 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17452 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17454 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17455 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17456 this.touchViewBodyEl.setStyle('overflow', 'auto');
17458 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17459 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17461 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17462 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17466 showTouchView : function()
17472 this.touchViewHeaderEl.hide();
17474 if(this.modalTitle.length){
17475 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17476 this.touchViewHeaderEl.show();
17479 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17480 this.touchViewEl.show();
17482 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17484 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17485 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17487 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17489 if(this.modalTitle.length){
17490 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17493 this.touchViewBodyEl.setHeight(bodyHeight);
17497 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17499 this.touchViewEl.addClass(['in','show']);
17502 if(this._touchViewMask){
17503 Roo.get(document.body).addClass("x-body-masked");
17504 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17505 this._touchViewMask.setStyle('z-index', 10000);
17506 this._touchViewMask.addClass('show');
17509 this.doTouchViewQuery();
17513 hideTouchView : function()
17515 this.touchViewEl.removeClass(['in','show']);
17519 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17521 this.touchViewEl.setStyle('display', 'none');
17524 if(this._touchViewMask){
17525 this._touchViewMask.removeClass('show');
17526 Roo.get(document.body).removeClass("x-body-masked");
17530 setTouchViewValue : function()
17537 Roo.each(this.tickItems, function(o){
17542 this.hideTouchView();
17545 doTouchViewQuery : function()
17554 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17558 if(!this.alwaysQuery || this.mode == 'local'){
17559 this.onTouchViewLoad();
17566 onTouchViewBeforeLoad : function(combo,opts)
17572 onTouchViewLoad : function()
17574 if(this.store.getCount() < 1){
17575 this.onTouchViewEmptyResults();
17579 this.clearTouchView();
17581 var rawValue = this.getRawValue();
17583 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17585 this.tickItems = [];
17587 this.store.data.each(function(d, rowIndex){
17588 var row = this.touchViewListGroup.createChild(template);
17590 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17591 row.addClass(d.data.cls);
17594 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17597 html : d.data[this.displayField]
17600 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17601 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17604 row.removeClass('selected');
17605 if(!this.multiple && this.valueField &&
17606 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17609 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17610 row.addClass('selected');
17613 if(this.multiple && this.valueField &&
17614 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17618 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17619 this.tickItems.push(d.data);
17622 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17626 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17628 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17630 if(this.modalTitle.length){
17631 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17634 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17636 if(this.mobile_restrict_height && listHeight < bodyHeight){
17637 this.touchViewBodyEl.setHeight(listHeight);
17642 if(firstChecked && listHeight > bodyHeight){
17643 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17648 onTouchViewLoadException : function()
17650 this.hideTouchView();
17653 onTouchViewEmptyResults : function()
17655 this.clearTouchView();
17657 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17659 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17663 clearTouchView : function()
17665 this.touchViewListGroup.dom.innerHTML = '';
17668 onTouchViewClick : function(e, el, o)
17670 e.preventDefault();
17673 var rowIndex = o.rowIndex;
17675 var r = this.store.getAt(rowIndex);
17677 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17679 if(!this.multiple){
17680 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17681 c.dom.removeAttribute('checked');
17684 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17686 this.setFromData(r.data);
17688 var close = this.closeTriggerEl();
17694 this.hideTouchView();
17696 this.fireEvent('select', this, r, rowIndex);
17701 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17702 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17703 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17707 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17708 this.addItem(r.data);
17709 this.tickItems.push(r.data);
17713 getAutoCreateNativeIOS : function()
17716 cls: 'form-group' //input-group,
17721 cls : 'roo-ios-select'
17725 combobox.name = this.name;
17728 if (this.disabled) {
17729 combobox.disabled = true;
17732 var settings = this;
17734 ['xs','sm','md','lg'].map(function(size){
17735 if (settings[size]) {
17736 cfg.cls += ' col-' + size + '-' + settings[size];
17746 initIOSView : function()
17748 this.store.on('load', this.onIOSViewLoad, this);
17753 onIOSViewLoad : function()
17755 if(this.store.getCount() < 1){
17759 this.clearIOSView();
17761 if(this.allowBlank) {
17763 var default_text = '-- SELECT --';
17765 if(this.placeholder.length){
17766 default_text = this.placeholder;
17769 if(this.emptyTitle.length){
17770 default_text += ' - ' + this.emptyTitle + ' -';
17773 var opt = this.inputEl().createChild({
17776 html : default_text
17780 o[this.valueField] = 0;
17781 o[this.displayField] = default_text;
17783 this.ios_options.push({
17790 this.store.data.each(function(d, rowIndex){
17794 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17795 html = d.data[this.displayField];
17800 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17801 value = d.data[this.valueField];
17810 if(this.value == d.data[this.valueField]){
17811 option['selected'] = true;
17814 var opt = this.inputEl().createChild(option);
17816 this.ios_options.push({
17823 this.inputEl().on('change', function(){
17824 this.fireEvent('select', this);
17829 clearIOSView: function()
17831 this.inputEl().dom.innerHTML = '';
17833 this.ios_options = [];
17836 setIOSValue: function(v)
17840 if(!this.ios_options){
17844 Roo.each(this.ios_options, function(opts){
17846 opts.el.dom.removeAttribute('selected');
17848 if(opts.data[this.valueField] != v){
17852 opts.el.dom.setAttribute('selected', true);
17858 * @cfg {Boolean} grow
17862 * @cfg {Number} growMin
17866 * @cfg {Number} growMax
17875 Roo.apply(Roo.bootstrap.ComboBox, {
17879 cls: 'modal-header',
17901 cls: 'list-group-item',
17905 cls: 'roo-combobox-list-group-item-value'
17909 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17923 listItemCheckbox : {
17925 cls: 'list-group-item',
17929 cls: 'roo-combobox-list-group-item-value'
17933 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17949 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17954 cls: 'modal-footer',
17962 cls: 'col-xs-6 text-left',
17965 cls: 'btn btn-danger roo-touch-view-cancel',
17971 cls: 'col-xs-6 text-right',
17974 cls: 'btn btn-success roo-touch-view-ok',
17985 Roo.apply(Roo.bootstrap.ComboBox, {
17987 touchViewTemplate : {
17989 cls: 'modal fade roo-combobox-touch-view',
17993 cls: 'modal-dialog',
17994 style : 'position:fixed', // we have to fix position....
17998 cls: 'modal-content',
18000 Roo.bootstrap.ComboBox.header,
18001 Roo.bootstrap.ComboBox.body,
18002 Roo.bootstrap.ComboBox.footer
18011 * Ext JS Library 1.1.1
18012 * Copyright(c) 2006-2007, Ext JS, LLC.
18014 * Originally Released Under LGPL - original licence link has changed is not relivant.
18017 * <script type="text/javascript">
18022 * @extends Roo.util.Observable
18023 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18024 * This class also supports single and multi selection modes. <br>
18025 * Create a data model bound view:
18027 var store = new Roo.data.Store(...);
18029 var view = new Roo.View({
18031 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18033 singleSelect: true,
18034 selectedClass: "ydataview-selected",
18038 // listen for node click?
18039 view.on("click", function(vw, index, node, e){
18040 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18044 dataModel.load("foobar.xml");
18046 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18048 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18049 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18051 * Note: old style constructor is still suported (container, template, config)
18054 * Create a new View
18055 * @param {Object} config The config object
18058 Roo.View = function(config, depreciated_tpl, depreciated_config){
18060 this.parent = false;
18062 if (typeof(depreciated_tpl) == 'undefined') {
18063 // new way.. - universal constructor.
18064 Roo.apply(this, config);
18065 this.el = Roo.get(this.el);
18068 this.el = Roo.get(config);
18069 this.tpl = depreciated_tpl;
18070 Roo.apply(this, depreciated_config);
18072 this.wrapEl = this.el.wrap().wrap();
18073 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18076 if(typeof(this.tpl) == "string"){
18077 this.tpl = new Roo.Template(this.tpl);
18079 // support xtype ctors..
18080 this.tpl = new Roo.factory(this.tpl, Roo);
18084 this.tpl.compile();
18089 * @event beforeclick
18090 * Fires before a click is processed. Returns false to cancel the default action.
18091 * @param {Roo.View} this
18092 * @param {Number} index The index of the target node
18093 * @param {HTMLElement} node The target node
18094 * @param {Roo.EventObject} e The raw event object
18096 "beforeclick" : true,
18099 * Fires when a template node is clicked.
18100 * @param {Roo.View} this
18101 * @param {Number} index The index of the target node
18102 * @param {HTMLElement} node The target node
18103 * @param {Roo.EventObject} e The raw event object
18108 * Fires when a template node is double clicked.
18109 * @param {Roo.View} this
18110 * @param {Number} index The index of the target node
18111 * @param {HTMLElement} node The target node
18112 * @param {Roo.EventObject} e The raw event object
18116 * @event contextmenu
18117 * Fires when a template node is right clicked.
18118 * @param {Roo.View} this
18119 * @param {Number} index The index of the target node
18120 * @param {HTMLElement} node The target node
18121 * @param {Roo.EventObject} e The raw event object
18123 "contextmenu" : true,
18125 * @event selectionchange
18126 * Fires when the selected nodes change.
18127 * @param {Roo.View} this
18128 * @param {Array} selections Array of the selected nodes
18130 "selectionchange" : true,
18133 * @event beforeselect
18134 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18135 * @param {Roo.View} this
18136 * @param {HTMLElement} node The node to be selected
18137 * @param {Array} selections Array of currently selected nodes
18139 "beforeselect" : true,
18141 * @event preparedata
18142 * Fires on every row to render, to allow you to change the data.
18143 * @param {Roo.View} this
18144 * @param {Object} data to be rendered (change this)
18146 "preparedata" : true
18154 "click": this.onClick,
18155 "dblclick": this.onDblClick,
18156 "contextmenu": this.onContextMenu,
18160 this.selections = [];
18162 this.cmp = new Roo.CompositeElementLite([]);
18164 this.store = Roo.factory(this.store, Roo.data);
18165 this.setStore(this.store, true);
18168 if ( this.footer && this.footer.xtype) {
18170 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18172 this.footer.dataSource = this.store;
18173 this.footer.container = fctr;
18174 this.footer = Roo.factory(this.footer, Roo);
18175 fctr.insertFirst(this.el);
18177 // this is a bit insane - as the paging toolbar seems to detach the el..
18178 // dom.parentNode.parentNode.parentNode
18179 // they get detached?
18183 Roo.View.superclass.constructor.call(this);
18188 Roo.extend(Roo.View, Roo.util.Observable, {
18191 * @cfg {Roo.data.Store} store Data store to load data from.
18196 * @cfg {String|Roo.Element} el The container element.
18201 * @cfg {String|Roo.Template} tpl The template used by this View
18205 * @cfg {String} dataName the named area of the template to use as the data area
18206 * Works with domtemplates roo-name="name"
18210 * @cfg {String} selectedClass The css class to add to selected nodes
18212 selectedClass : "x-view-selected",
18214 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18219 * @cfg {String} text to display on mask (default Loading)
18223 * @cfg {Boolean} multiSelect Allow multiple selection
18225 multiSelect : false,
18227 * @cfg {Boolean} singleSelect Allow single selection
18229 singleSelect: false,
18232 * @cfg {Boolean} toggleSelect - selecting
18234 toggleSelect : false,
18237 * @cfg {Boolean} tickable - selecting
18242 * Returns the element this view is bound to.
18243 * @return {Roo.Element}
18245 getEl : function(){
18246 return this.wrapEl;
18252 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18254 refresh : function(){
18255 //Roo.log('refresh');
18258 // if we are using something like 'domtemplate', then
18259 // the what gets used is:
18260 // t.applySubtemplate(NAME, data, wrapping data..)
18261 // the outer template then get' applied with
18262 // the store 'extra data'
18263 // and the body get's added to the
18264 // roo-name="data" node?
18265 // <span class='roo-tpl-{name}'></span> ?????
18269 this.clearSelections();
18270 this.el.update("");
18272 var records = this.store.getRange();
18273 if(records.length < 1) {
18275 // is this valid?? = should it render a template??
18277 this.el.update(this.emptyText);
18281 if (this.dataName) {
18282 this.el.update(t.apply(this.store.meta)); //????
18283 el = this.el.child('.roo-tpl-' + this.dataName);
18286 for(var i = 0, len = records.length; i < len; i++){
18287 var data = this.prepareData(records[i].data, i, records[i]);
18288 this.fireEvent("preparedata", this, data, i, records[i]);
18290 var d = Roo.apply({}, data);
18293 Roo.apply(d, {'roo-id' : Roo.id()});
18297 Roo.each(this.parent.item, function(item){
18298 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18301 Roo.apply(d, {'roo-data-checked' : 'checked'});
18305 html[html.length] = Roo.util.Format.trim(
18307 t.applySubtemplate(this.dataName, d, this.store.meta) :
18314 el.update(html.join(""));
18315 this.nodes = el.dom.childNodes;
18316 this.updateIndexes(0);
18321 * Function to override to reformat the data that is sent to
18322 * the template for each node.
18323 * DEPRICATED - use the preparedata event handler.
18324 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18325 * a JSON object for an UpdateManager bound view).
18327 prepareData : function(data, index, record)
18329 this.fireEvent("preparedata", this, data, index, record);
18333 onUpdate : function(ds, record){
18334 // Roo.log('on update');
18335 this.clearSelections();
18336 var index = this.store.indexOf(record);
18337 var n = this.nodes[index];
18338 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18339 n.parentNode.removeChild(n);
18340 this.updateIndexes(index, index);
18346 onAdd : function(ds, records, index)
18348 //Roo.log(['on Add', ds, records, index] );
18349 this.clearSelections();
18350 if(this.nodes.length == 0){
18354 var n = this.nodes[index];
18355 for(var i = 0, len = records.length; i < len; i++){
18356 var d = this.prepareData(records[i].data, i, records[i]);
18358 this.tpl.insertBefore(n, d);
18361 this.tpl.append(this.el, d);
18364 this.updateIndexes(index);
18367 onRemove : function(ds, record, index){
18368 // Roo.log('onRemove');
18369 this.clearSelections();
18370 var el = this.dataName ?
18371 this.el.child('.roo-tpl-' + this.dataName) :
18374 el.dom.removeChild(this.nodes[index]);
18375 this.updateIndexes(index);
18379 * Refresh an individual node.
18380 * @param {Number} index
18382 refreshNode : function(index){
18383 this.onUpdate(this.store, this.store.getAt(index));
18386 updateIndexes : function(startIndex, endIndex){
18387 var ns = this.nodes;
18388 startIndex = startIndex || 0;
18389 endIndex = endIndex || ns.length - 1;
18390 for(var i = startIndex; i <= endIndex; i++){
18391 ns[i].nodeIndex = i;
18396 * Changes the data store this view uses and refresh the view.
18397 * @param {Store} store
18399 setStore : function(store, initial){
18400 if(!initial && this.store){
18401 this.store.un("datachanged", this.refresh);
18402 this.store.un("add", this.onAdd);
18403 this.store.un("remove", this.onRemove);
18404 this.store.un("update", this.onUpdate);
18405 this.store.un("clear", this.refresh);
18406 this.store.un("beforeload", this.onBeforeLoad);
18407 this.store.un("load", this.onLoad);
18408 this.store.un("loadexception", this.onLoad);
18412 store.on("datachanged", this.refresh, this);
18413 store.on("add", this.onAdd, this);
18414 store.on("remove", this.onRemove, this);
18415 store.on("update", this.onUpdate, this);
18416 store.on("clear", this.refresh, this);
18417 store.on("beforeload", this.onBeforeLoad, this);
18418 store.on("load", this.onLoad, this);
18419 store.on("loadexception", this.onLoad, this);
18427 * onbeforeLoad - masks the loading area.
18430 onBeforeLoad : function(store,opts)
18432 //Roo.log('onBeforeLoad');
18434 this.el.update("");
18436 this.el.mask(this.mask ? this.mask : "Loading" );
18438 onLoad : function ()
18445 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18446 * @param {HTMLElement} node
18447 * @return {HTMLElement} The template node
18449 findItemFromChild : function(node){
18450 var el = this.dataName ?
18451 this.el.child('.roo-tpl-' + this.dataName,true) :
18454 if(!node || node.parentNode == el){
18457 var p = node.parentNode;
18458 while(p && p != el){
18459 if(p.parentNode == el){
18468 onClick : function(e){
18469 var item = this.findItemFromChild(e.getTarget());
18471 var index = this.indexOf(item);
18472 if(this.onItemClick(item, index, e) !== false){
18473 this.fireEvent("click", this, index, item, e);
18476 this.clearSelections();
18481 onContextMenu : function(e){
18482 var item = this.findItemFromChild(e.getTarget());
18484 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18489 onDblClick : function(e){
18490 var item = this.findItemFromChild(e.getTarget());
18492 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18496 onItemClick : function(item, index, e)
18498 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18501 if (this.toggleSelect) {
18502 var m = this.isSelected(item) ? 'unselect' : 'select';
18505 _t[m](item, true, false);
18508 if(this.multiSelect || this.singleSelect){
18509 if(this.multiSelect && e.shiftKey && this.lastSelection){
18510 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18512 this.select(item, this.multiSelect && e.ctrlKey);
18513 this.lastSelection = item;
18516 if(!this.tickable){
18517 e.preventDefault();
18525 * Get the number of selected nodes.
18528 getSelectionCount : function(){
18529 return this.selections.length;
18533 * Get the currently selected nodes.
18534 * @return {Array} An array of HTMLElements
18536 getSelectedNodes : function(){
18537 return this.selections;
18541 * Get the indexes of the selected nodes.
18544 getSelectedIndexes : function(){
18545 var indexes = [], s = this.selections;
18546 for(var i = 0, len = s.length; i < len; i++){
18547 indexes.push(s[i].nodeIndex);
18553 * Clear all selections
18554 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18556 clearSelections : function(suppressEvent){
18557 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18558 this.cmp.elements = this.selections;
18559 this.cmp.removeClass(this.selectedClass);
18560 this.selections = [];
18561 if(!suppressEvent){
18562 this.fireEvent("selectionchange", this, this.selections);
18568 * Returns true if the passed node is selected
18569 * @param {HTMLElement/Number} node The node or node index
18570 * @return {Boolean}
18572 isSelected : function(node){
18573 var s = this.selections;
18577 node = this.getNode(node);
18578 return s.indexOf(node) !== -1;
18583 * @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
18584 * @param {Boolean} keepExisting (optional) true to keep existing selections
18585 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18587 select : function(nodeInfo, keepExisting, suppressEvent){
18588 if(nodeInfo instanceof Array){
18590 this.clearSelections(true);
18592 for(var i = 0, len = nodeInfo.length; i < len; i++){
18593 this.select(nodeInfo[i], true, true);
18597 var node = this.getNode(nodeInfo);
18598 if(!node || this.isSelected(node)){
18599 return; // already selected.
18602 this.clearSelections(true);
18605 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18606 Roo.fly(node).addClass(this.selectedClass);
18607 this.selections.push(node);
18608 if(!suppressEvent){
18609 this.fireEvent("selectionchange", this, this.selections);
18617 * @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
18618 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18619 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18621 unselect : function(nodeInfo, keepExisting, suppressEvent)
18623 if(nodeInfo instanceof Array){
18624 Roo.each(this.selections, function(s) {
18625 this.unselect(s, nodeInfo);
18629 var node = this.getNode(nodeInfo);
18630 if(!node || !this.isSelected(node)){
18631 //Roo.log("not selected");
18632 return; // not selected.
18636 Roo.each(this.selections, function(s) {
18638 Roo.fly(node).removeClass(this.selectedClass);
18645 this.selections= ns;
18646 this.fireEvent("selectionchange", this, this.selections);
18650 * Gets a template node.
18651 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18652 * @return {HTMLElement} The node or null if it wasn't found
18654 getNode : function(nodeInfo){
18655 if(typeof nodeInfo == "string"){
18656 return document.getElementById(nodeInfo);
18657 }else if(typeof nodeInfo == "number"){
18658 return this.nodes[nodeInfo];
18664 * Gets a range template nodes.
18665 * @param {Number} startIndex
18666 * @param {Number} endIndex
18667 * @return {Array} An array of nodes
18669 getNodes : function(start, end){
18670 var ns = this.nodes;
18671 start = start || 0;
18672 end = typeof end == "undefined" ? ns.length - 1 : end;
18675 for(var i = start; i <= end; i++){
18679 for(var i = start; i >= end; i--){
18687 * Finds the index of the passed node
18688 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18689 * @return {Number} The index of the node or -1
18691 indexOf : function(node){
18692 node = this.getNode(node);
18693 if(typeof node.nodeIndex == "number"){
18694 return node.nodeIndex;
18696 var ns = this.nodes;
18697 for(var i = 0, len = ns.length; i < len; i++){
18708 * based on jquery fullcalendar
18712 Roo.bootstrap = Roo.bootstrap || {};
18714 * @class Roo.bootstrap.Calendar
18715 * @extends Roo.bootstrap.Component
18716 * Bootstrap Calendar class
18717 * @cfg {Boolean} loadMask (true|false) default false
18718 * @cfg {Object} header generate the user specific header of the calendar, default false
18721 * Create a new Container
18722 * @param {Object} config The config object
18727 Roo.bootstrap.Calendar = function(config){
18728 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18732 * Fires when a date is selected
18733 * @param {DatePicker} this
18734 * @param {Date} date The selected date
18738 * @event monthchange
18739 * Fires when the displayed month changes
18740 * @param {DatePicker} this
18741 * @param {Date} date The selected month
18743 'monthchange': true,
18745 * @event evententer
18746 * Fires when mouse over an event
18747 * @param {Calendar} this
18748 * @param {event} Event
18750 'evententer': true,
18752 * @event eventleave
18753 * Fires when the mouse leaves an
18754 * @param {Calendar} this
18757 'eventleave': true,
18759 * @event eventclick
18760 * Fires when the mouse click an
18761 * @param {Calendar} this
18770 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18773 * @cfg {Number} startDay
18774 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18782 getAutoCreate : function(){
18785 var fc_button = function(name, corner, style, content ) {
18786 return Roo.apply({},{
18788 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18790 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18793 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18804 style : 'width:100%',
18811 cls : 'fc-header-left',
18813 fc_button('prev', 'left', 'arrow', '‹' ),
18814 fc_button('next', 'right', 'arrow', '›' ),
18815 { tag: 'span', cls: 'fc-header-space' },
18816 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18824 cls : 'fc-header-center',
18828 cls: 'fc-header-title',
18831 html : 'month / year'
18839 cls : 'fc-header-right',
18841 /* fc_button('month', 'left', '', 'month' ),
18842 fc_button('week', '', '', 'week' ),
18843 fc_button('day', 'right', '', 'day' )
18855 header = this.header;
18858 var cal_heads = function() {
18860 // fixme - handle this.
18862 for (var i =0; i < Date.dayNames.length; i++) {
18863 var d = Date.dayNames[i];
18866 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18867 html : d.substring(0,3)
18871 ret[0].cls += ' fc-first';
18872 ret[6].cls += ' fc-last';
18875 var cal_cell = function(n) {
18878 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18883 cls: 'fc-day-number',
18887 cls: 'fc-day-content',
18891 style: 'position: relative;' // height: 17px;
18903 var cal_rows = function() {
18906 for (var r = 0; r < 6; r++) {
18913 for (var i =0; i < Date.dayNames.length; i++) {
18914 var d = Date.dayNames[i];
18915 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18918 row.cn[0].cls+=' fc-first';
18919 row.cn[0].cn[0].style = 'min-height:90px';
18920 row.cn[6].cls+=' fc-last';
18924 ret[0].cls += ' fc-first';
18925 ret[4].cls += ' fc-prev-last';
18926 ret[5].cls += ' fc-last';
18933 cls: 'fc-border-separate',
18934 style : 'width:100%',
18942 cls : 'fc-first fc-last',
18960 cls : 'fc-content',
18961 style : "position: relative;",
18964 cls : 'fc-view fc-view-month fc-grid',
18965 style : 'position: relative',
18966 unselectable : 'on',
18969 cls : 'fc-event-container',
18970 style : 'position:absolute;z-index:8;top:0;left:0;'
18988 initEvents : function()
18991 throw "can not find store for calendar";
18997 style: "text-align:center",
19001 style: "background-color:white;width:50%;margin:250 auto",
19005 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19016 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19018 var size = this.el.select('.fc-content', true).first().getSize();
19019 this.maskEl.setSize(size.width, size.height);
19020 this.maskEl.enableDisplayMode("block");
19021 if(!this.loadMask){
19022 this.maskEl.hide();
19025 this.store = Roo.factory(this.store, Roo.data);
19026 this.store.on('load', this.onLoad, this);
19027 this.store.on('beforeload', this.onBeforeLoad, this);
19031 this.cells = this.el.select('.fc-day',true);
19032 //Roo.log(this.cells);
19033 this.textNodes = this.el.query('.fc-day-number');
19034 this.cells.addClassOnOver('fc-state-hover');
19036 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19037 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19038 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19039 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19041 this.on('monthchange', this.onMonthChange, this);
19043 this.update(new Date().clearTime());
19046 resize : function() {
19047 var sz = this.el.getSize();
19049 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19050 this.el.select('.fc-day-content div',true).setHeight(34);
19055 showPrevMonth : function(e){
19056 this.update(this.activeDate.add("mo", -1));
19058 showToday : function(e){
19059 this.update(new Date().clearTime());
19062 showNextMonth : function(e){
19063 this.update(this.activeDate.add("mo", 1));
19067 showPrevYear : function(){
19068 this.update(this.activeDate.add("y", -1));
19072 showNextYear : function(){
19073 this.update(this.activeDate.add("y", 1));
19078 update : function(date)
19080 var vd = this.activeDate;
19081 this.activeDate = date;
19082 // if(vd && this.el){
19083 // var t = date.getTime();
19084 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19085 // Roo.log('using add remove');
19087 // this.fireEvent('monthchange', this, date);
19089 // this.cells.removeClass("fc-state-highlight");
19090 // this.cells.each(function(c){
19091 // if(c.dateValue == t){
19092 // c.addClass("fc-state-highlight");
19093 // setTimeout(function(){
19094 // try{c.dom.firstChild.focus();}catch(e){}
19104 var days = date.getDaysInMonth();
19106 var firstOfMonth = date.getFirstDateOfMonth();
19107 var startingPos = firstOfMonth.getDay()-this.startDay;
19109 if(startingPos < this.startDay){
19113 var pm = date.add(Date.MONTH, -1);
19114 var prevStart = pm.getDaysInMonth()-startingPos;
19116 this.cells = this.el.select('.fc-day',true);
19117 this.textNodes = this.el.query('.fc-day-number');
19118 this.cells.addClassOnOver('fc-state-hover');
19120 var cells = this.cells.elements;
19121 var textEls = this.textNodes;
19123 Roo.each(cells, function(cell){
19124 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19127 days += startingPos;
19129 // convert everything to numbers so it's fast
19130 var day = 86400000;
19131 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19134 //Roo.log(prevStart);
19136 var today = new Date().clearTime().getTime();
19137 var sel = date.clearTime().getTime();
19138 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19139 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19140 var ddMatch = this.disabledDatesRE;
19141 var ddText = this.disabledDatesText;
19142 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19143 var ddaysText = this.disabledDaysText;
19144 var format = this.format;
19146 var setCellClass = function(cal, cell){
19150 //Roo.log('set Cell Class');
19152 var t = d.getTime();
19156 cell.dateValue = t;
19158 cell.className += " fc-today";
19159 cell.className += " fc-state-highlight";
19160 cell.title = cal.todayText;
19163 // disable highlight in other month..
19164 //cell.className += " fc-state-highlight";
19169 cell.className = " fc-state-disabled";
19170 cell.title = cal.minText;
19174 cell.className = " fc-state-disabled";
19175 cell.title = cal.maxText;
19179 if(ddays.indexOf(d.getDay()) != -1){
19180 cell.title = ddaysText;
19181 cell.className = " fc-state-disabled";
19184 if(ddMatch && format){
19185 var fvalue = d.dateFormat(format);
19186 if(ddMatch.test(fvalue)){
19187 cell.title = ddText.replace("%0", fvalue);
19188 cell.className = " fc-state-disabled";
19192 if (!cell.initialClassName) {
19193 cell.initialClassName = cell.dom.className;
19196 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19201 for(; i < startingPos; i++) {
19202 textEls[i].innerHTML = (++prevStart);
19203 d.setDate(d.getDate()+1);
19205 cells[i].className = "fc-past fc-other-month";
19206 setCellClass(this, cells[i]);
19211 for(; i < days; i++){
19212 intDay = i - startingPos + 1;
19213 textEls[i].innerHTML = (intDay);
19214 d.setDate(d.getDate()+1);
19216 cells[i].className = ''; // "x-date-active";
19217 setCellClass(this, cells[i]);
19221 for(; i < 42; i++) {
19222 textEls[i].innerHTML = (++extraDays);
19223 d.setDate(d.getDate()+1);
19225 cells[i].className = "fc-future fc-other-month";
19226 setCellClass(this, cells[i]);
19229 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19231 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19233 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19234 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19236 if(totalRows != 6){
19237 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19238 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19241 this.fireEvent('monthchange', this, date);
19245 if(!this.internalRender){
19246 var main = this.el.dom.firstChild;
19247 var w = main.offsetWidth;
19248 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19249 Roo.fly(main).setWidth(w);
19250 this.internalRender = true;
19251 // opera does not respect the auto grow header center column
19252 // then, after it gets a width opera refuses to recalculate
19253 // without a second pass
19254 if(Roo.isOpera && !this.secondPass){
19255 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19256 this.secondPass = true;
19257 this.update.defer(10, this, [date]);
19264 findCell : function(dt) {
19265 dt = dt.clearTime().getTime();
19267 this.cells.each(function(c){
19268 //Roo.log("check " +c.dateValue + '?=' + dt);
19269 if(c.dateValue == dt){
19279 findCells : function(ev) {
19280 var s = ev.start.clone().clearTime().getTime();
19282 var e= ev.end.clone().clearTime().getTime();
19285 this.cells.each(function(c){
19286 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19288 if(c.dateValue > e){
19291 if(c.dateValue < s){
19300 // findBestRow: function(cells)
19304 // for (var i =0 ; i < cells.length;i++) {
19305 // ret = Math.max(cells[i].rows || 0,ret);
19312 addItem : function(ev)
19314 // look for vertical location slot in
19315 var cells = this.findCells(ev);
19317 // ev.row = this.findBestRow(cells);
19319 // work out the location.
19323 for(var i =0; i < cells.length; i++) {
19325 cells[i].row = cells[0].row;
19328 cells[i].row = cells[i].row + 1;
19338 if (crow.start.getY() == cells[i].getY()) {
19340 crow.end = cells[i];
19357 cells[0].events.push(ev);
19359 this.calevents.push(ev);
19362 clearEvents: function() {
19364 if(!this.calevents){
19368 Roo.each(this.cells.elements, function(c){
19374 Roo.each(this.calevents, function(e) {
19375 Roo.each(e.els, function(el) {
19376 el.un('mouseenter' ,this.onEventEnter, this);
19377 el.un('mouseleave' ,this.onEventLeave, this);
19382 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19388 renderEvents: function()
19392 this.cells.each(function(c) {
19401 if(c.row != c.events.length){
19402 r = 4 - (4 - (c.row - c.events.length));
19405 c.events = ev.slice(0, r);
19406 c.more = ev.slice(r);
19408 if(c.more.length && c.more.length == 1){
19409 c.events.push(c.more.pop());
19412 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19416 this.cells.each(function(c) {
19418 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19421 for (var e = 0; e < c.events.length; e++){
19422 var ev = c.events[e];
19423 var rows = ev.rows;
19425 for(var i = 0; i < rows.length; i++) {
19427 // how many rows should it span..
19430 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19431 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19433 unselectable : "on",
19436 cls: 'fc-event-inner',
19440 // cls: 'fc-event-time',
19441 // html : cells.length > 1 ? '' : ev.time
19445 cls: 'fc-event-title',
19446 html : String.format('{0}', ev.title)
19453 cls: 'ui-resizable-handle ui-resizable-e',
19454 html : '  '
19461 cfg.cls += ' fc-event-start';
19463 if ((i+1) == rows.length) {
19464 cfg.cls += ' fc-event-end';
19467 var ctr = _this.el.select('.fc-event-container',true).first();
19468 var cg = ctr.createChild(cfg);
19470 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19471 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19473 var r = (c.more.length) ? 1 : 0;
19474 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19475 cg.setWidth(ebox.right - sbox.x -2);
19477 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19478 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19479 cg.on('click', _this.onEventClick, _this, ev);
19490 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19491 style : 'position: absolute',
19492 unselectable : "on",
19495 cls: 'fc-event-inner',
19499 cls: 'fc-event-title',
19507 cls: 'ui-resizable-handle ui-resizable-e',
19508 html : '  '
19514 var ctr = _this.el.select('.fc-event-container',true).first();
19515 var cg = ctr.createChild(cfg);
19517 var sbox = c.select('.fc-day-content',true).first().getBox();
19518 var ebox = c.select('.fc-day-content',true).first().getBox();
19520 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19521 cg.setWidth(ebox.right - sbox.x -2);
19523 cg.on('click', _this.onMoreEventClick, _this, c.more);
19533 onEventEnter: function (e, el,event,d) {
19534 this.fireEvent('evententer', this, el, event);
19537 onEventLeave: function (e, el,event,d) {
19538 this.fireEvent('eventleave', this, el, event);
19541 onEventClick: function (e, el,event,d) {
19542 this.fireEvent('eventclick', this, el, event);
19545 onMonthChange: function () {
19549 onMoreEventClick: function(e, el, more)
19553 this.calpopover.placement = 'right';
19554 this.calpopover.setTitle('More');
19556 this.calpopover.setContent('');
19558 var ctr = this.calpopover.el.select('.popover-content', true).first();
19560 Roo.each(more, function(m){
19562 cls : 'fc-event-hori fc-event-draggable',
19565 var cg = ctr.createChild(cfg);
19567 cg.on('click', _this.onEventClick, _this, m);
19570 this.calpopover.show(el);
19575 onLoad: function ()
19577 this.calevents = [];
19580 if(this.store.getCount() > 0){
19581 this.store.data.each(function(d){
19584 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19585 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19586 time : d.data.start_time,
19587 title : d.data.title,
19588 description : d.data.description,
19589 venue : d.data.venue
19594 this.renderEvents();
19596 if(this.calevents.length && this.loadMask){
19597 this.maskEl.hide();
19601 onBeforeLoad: function()
19603 this.clearEvents();
19605 this.maskEl.show();
19619 * @class Roo.bootstrap.Popover
19620 * @extends Roo.bootstrap.Component
19621 * Bootstrap Popover class
19622 * @cfg {String} html contents of the popover (or false to use children..)
19623 * @cfg {String} title of popover (or false to hide)
19624 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19625 * @cfg {String} trigger click || hover (or false to trigger manually)
19626 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19627 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19628 * - if false and it has a 'parent' then it will be automatically added to that element
19629 * - if string - Roo.get will be called
19630 * @cfg {Number} delay - delay before showing
19633 * Create a new Popover
19634 * @param {Object} config The config object
19637 Roo.bootstrap.Popover = function(config){
19638 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19644 * After the popover show
19646 * @param {Roo.bootstrap.Popover} this
19651 * After the popover hide
19653 * @param {Roo.bootstrap.Popover} this
19659 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19664 placement : 'right',
19665 trigger : 'hover', // hover
19671 can_build_overlaid : false,
19673 maskEl : false, // the mask element
19676 alignEl : false, // when show is called with an element - this get's stored.
19678 getChildContainer : function()
19680 return this.contentEl;
19683 getPopoverHeader : function()
19685 this.title = true; // flag not to hide it..
19686 this.headerEl.addClass('p-0');
19687 return this.headerEl
19691 getAutoCreate : function(){
19694 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19695 style: 'display:block',
19701 cls : 'popover-inner ',
19705 cls: 'popover-title popover-header',
19706 html : this.title === false ? '' : this.title
19709 cls : 'popover-content popover-body ' + (this.cls || ''),
19710 html : this.html || ''
19721 * @param {string} the title
19723 setTitle: function(str)
19727 this.headerEl.dom.innerHTML = str;
19732 * @param {string} the body content
19734 setContent: function(str)
19737 if (this.contentEl) {
19738 this.contentEl.dom.innerHTML = str;
19742 // as it get's added to the bottom of the page.
19743 onRender : function(ct, position)
19745 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19750 var cfg = Roo.apply({}, this.getAutoCreate());
19754 cfg.cls += ' ' + this.cls;
19757 cfg.style = this.style;
19759 //Roo.log("adding to ");
19760 this.el = Roo.get(document.body).createChild(cfg, position);
19761 // Roo.log(this.el);
19764 this.contentEl = this.el.select('.popover-content',true).first();
19765 this.headerEl = this.el.select('.popover-title',true).first();
19768 if(typeof(this.items) != 'undefined'){
19769 var items = this.items;
19772 for(var i =0;i < items.length;i++) {
19773 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19777 this.items = nitems;
19779 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19780 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19787 resizeMask : function()
19789 this.maskEl.setSize(
19790 Roo.lib.Dom.getViewWidth(true),
19791 Roo.lib.Dom.getViewHeight(true)
19795 initEvents : function()
19799 Roo.bootstrap.Popover.register(this);
19802 this.arrowEl = this.el.select('.arrow',true).first();
19803 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19804 this.el.enableDisplayMode('block');
19808 if (this.over === false && !this.parent()) {
19811 if (this.triggers === false) {
19816 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19817 var triggers = this.trigger ? this.trigger.split(' ') : [];
19818 Roo.each(triggers, function(trigger) {
19820 if (trigger == 'click') {
19821 on_el.on('click', this.toggle, this);
19822 } else if (trigger != 'manual') {
19823 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19824 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19826 on_el.on(eventIn ,this.enter, this);
19827 on_el.on(eventOut, this.leave, this);
19837 toggle : function () {
19838 this.hoverState == 'in' ? this.leave() : this.enter();
19841 enter : function () {
19843 clearTimeout(this.timeout);
19845 this.hoverState = 'in';
19847 if (!this.delay || !this.delay.show) {
19852 this.timeout = setTimeout(function () {
19853 if (_t.hoverState == 'in') {
19856 }, this.delay.show)
19859 leave : function() {
19860 clearTimeout(this.timeout);
19862 this.hoverState = 'out';
19864 if (!this.delay || !this.delay.hide) {
19869 this.timeout = setTimeout(function () {
19870 if (_t.hoverState == 'out') {
19873 }, this.delay.hide)
19877 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19878 * @param {string} (left|right|top|bottom) position
19880 show : function (on_el, placement)
19882 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19883 on_el = on_el || false; // default to false
19886 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19887 on_el = this.parent().el;
19888 } else if (this.over) {
19889 Roo.get(this.over);
19894 this.alignEl = Roo.get( on_el );
19897 this.render(document.body);
19903 if (this.title === false) {
19904 this.headerEl.hide();
19909 this.el.dom.style.display = 'block';
19912 if (this.alignEl) {
19913 this.updatePosition(this.placement, true);
19916 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19917 var es = this.el.getSize();
19918 var x = Roo.lib.Dom.getViewWidth()/2;
19919 var y = Roo.lib.Dom.getViewHeight()/2;
19920 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19925 //var arrow = this.el.select('.arrow',true).first();
19926 //arrow.set(align[2],
19928 this.el.addClass('in');
19932 this.hoverState = 'in';
19935 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19936 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19937 this.maskEl.dom.style.display = 'block';
19938 this.maskEl.addClass('show');
19940 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19942 this.fireEvent('show', this);
19946 * fire this manually after loading a grid in the table for example
19947 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19948 * @param {Boolean} try and move it if we cant get right position.
19950 updatePosition : function(placement, try_move)
19952 // allow for calling with no parameters
19953 placement = placement ? placement : this.placement;
19954 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19956 this.el.removeClass([
19957 'fade','top','bottom', 'left', 'right','in',
19958 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19960 this.el.addClass(placement + ' bs-popover-' + placement);
19962 if (!this.alignEl ) {
19966 switch (placement) {
19968 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19969 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19970 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19971 //normal display... or moved up/down.
19972 this.el.setXY(offset);
19973 var xy = this.alignEl.getAnchorXY('tr', false);
19975 this.arrowEl.setXY(xy);
19978 // continue through...
19979 return this.updatePosition('left', false);
19983 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19984 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19985 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19986 //normal display... or moved up/down.
19987 this.el.setXY(offset);
19988 var xy = this.alignEl.getAnchorXY('tl', false);
19989 xy[0]-=10;xy[1]+=5; // << fix me
19990 this.arrowEl.setXY(xy);
19994 return this.updatePosition('right', false);
19997 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
19998 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
19999 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20000 //normal display... or moved up/down.
20001 this.el.setXY(offset);
20002 var xy = this.alignEl.getAnchorXY('t', false);
20003 xy[1]-=10; // << fix me
20004 this.arrowEl.setXY(xy);
20008 return this.updatePosition('bottom', false);
20011 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20012 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20013 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20014 //normal display... or moved up/down.
20015 this.el.setXY(offset);
20016 var xy = this.alignEl.getAnchorXY('b', false);
20017 xy[1]+=2; // << fix me
20018 this.arrowEl.setXY(xy);
20022 return this.updatePosition('top', false);
20033 this.el.setXY([0,0]);
20034 this.el.removeClass('in');
20036 this.hoverState = null;
20037 this.maskEl.hide(); // always..
20038 this.fireEvent('hide', this);
20044 Roo.apply(Roo.bootstrap.Popover, {
20047 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20048 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20049 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20050 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20055 clickHander : false,
20058 onMouseDown : function(e)
20060 if (!e.getTarget(".roo-popover")) {
20068 register : function(popup)
20070 if (!Roo.bootstrap.Popover.clickHandler) {
20071 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20073 // hide other popups.
20075 this.popups.push(popup);
20077 hideAll : function()
20079 this.popups.forEach(function(p) {
20087 * Card header - holder for the card header elements.
20092 * @class Roo.bootstrap.PopoverNav
20093 * @extends Roo.bootstrap.NavGroup
20094 * Bootstrap Popover header navigation class
20096 * Create a new Popover Header Navigation
20097 * @param {Object} config The config object
20100 Roo.bootstrap.PopoverNav = function(config){
20101 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20104 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20107 container_method : 'getPopoverHeader'
20125 * @class Roo.bootstrap.Progress
20126 * @extends Roo.bootstrap.Component
20127 * Bootstrap Progress class
20128 * @cfg {Boolean} striped striped of the progress bar
20129 * @cfg {Boolean} active animated of the progress bar
20133 * Create a new Progress
20134 * @param {Object} config The config object
20137 Roo.bootstrap.Progress = function(config){
20138 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20141 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20146 getAutoCreate : function(){
20154 cfg.cls += ' progress-striped';
20158 cfg.cls += ' active';
20177 * @class Roo.bootstrap.ProgressBar
20178 * @extends Roo.bootstrap.Component
20179 * Bootstrap ProgressBar class
20180 * @cfg {Number} aria_valuenow aria-value now
20181 * @cfg {Number} aria_valuemin aria-value min
20182 * @cfg {Number} aria_valuemax aria-value max
20183 * @cfg {String} label label for the progress bar
20184 * @cfg {String} panel (success | info | warning | danger )
20185 * @cfg {String} role role of the progress bar
20186 * @cfg {String} sr_only text
20190 * Create a new ProgressBar
20191 * @param {Object} config The config object
20194 Roo.bootstrap.ProgressBar = function(config){
20195 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20198 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20202 aria_valuemax : 100,
20208 getAutoCreate : function()
20213 cls: 'progress-bar',
20214 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20226 cfg.role = this.role;
20229 if(this.aria_valuenow){
20230 cfg['aria-valuenow'] = this.aria_valuenow;
20233 if(this.aria_valuemin){
20234 cfg['aria-valuemin'] = this.aria_valuemin;
20237 if(this.aria_valuemax){
20238 cfg['aria-valuemax'] = this.aria_valuemax;
20241 if(this.label && !this.sr_only){
20242 cfg.html = this.label;
20246 cfg.cls += ' progress-bar-' + this.panel;
20252 update : function(aria_valuenow)
20254 this.aria_valuenow = aria_valuenow;
20256 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20271 * @class Roo.bootstrap.TabGroup
20272 * @extends Roo.bootstrap.Column
20273 * Bootstrap Column class
20274 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20275 * @cfg {Boolean} carousel true to make the group behave like a carousel
20276 * @cfg {Boolean} bullets show bullets for the panels
20277 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20278 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20279 * @cfg {Boolean} showarrow (true|false) show arrow default true
20282 * Create a new TabGroup
20283 * @param {Object} config The config object
20286 Roo.bootstrap.TabGroup = function(config){
20287 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20289 this.navId = Roo.id();
20292 Roo.bootstrap.TabGroup.register(this);
20296 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20299 transition : false,
20304 slideOnTouch : false,
20307 getAutoCreate : function()
20309 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20311 cfg.cls += ' tab-content';
20313 if (this.carousel) {
20314 cfg.cls += ' carousel slide';
20317 cls : 'carousel-inner',
20321 if(this.bullets && !Roo.isTouch){
20324 cls : 'carousel-bullets',
20328 if(this.bullets_cls){
20329 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20336 cfg.cn[0].cn.push(bullets);
20339 if(this.showarrow){
20340 cfg.cn[0].cn.push({
20342 class : 'carousel-arrow',
20346 class : 'carousel-prev',
20350 class : 'fa fa-chevron-left'
20356 class : 'carousel-next',
20360 class : 'fa fa-chevron-right'
20373 initEvents: function()
20375 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20376 // this.el.on("touchstart", this.onTouchStart, this);
20379 if(this.autoslide){
20382 this.slideFn = window.setInterval(function() {
20383 _this.showPanelNext();
20387 if(this.showarrow){
20388 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20389 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20395 // onTouchStart : function(e, el, o)
20397 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20401 // this.showPanelNext();
20405 getChildContainer : function()
20407 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20411 * register a Navigation item
20412 * @param {Roo.bootstrap.NavItem} the navitem to add
20414 register : function(item)
20416 this.tabs.push( item);
20417 item.navId = this.navId; // not really needed..
20422 getActivePanel : function()
20425 Roo.each(this.tabs, function(t) {
20435 getPanelByName : function(n)
20438 Roo.each(this.tabs, function(t) {
20439 if (t.tabId == n) {
20447 indexOfPanel : function(p)
20450 Roo.each(this.tabs, function(t,i) {
20451 if (t.tabId == p.tabId) {
20460 * show a specific panel
20461 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20462 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20464 showPanel : function (pan)
20466 if(this.transition || typeof(pan) == 'undefined'){
20467 Roo.log("waiting for the transitionend");
20471 if (typeof(pan) == 'number') {
20472 pan = this.tabs[pan];
20475 if (typeof(pan) == 'string') {
20476 pan = this.getPanelByName(pan);
20479 var cur = this.getActivePanel();
20482 Roo.log('pan or acitve pan is undefined');
20486 if (pan.tabId == this.getActivePanel().tabId) {
20490 if (false === cur.fireEvent('beforedeactivate')) {
20494 if(this.bullets > 0 && !Roo.isTouch){
20495 this.setActiveBullet(this.indexOfPanel(pan));
20498 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20500 //class="carousel-item carousel-item-next carousel-item-left"
20502 this.transition = true;
20503 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20504 var lr = dir == 'next' ? 'left' : 'right';
20505 pan.el.addClass(dir); // or prev
20506 pan.el.addClass('carousel-item-' + dir); // or prev
20507 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20508 cur.el.addClass(lr); // or right
20509 pan.el.addClass(lr);
20510 cur.el.addClass('carousel-item-' +lr); // or right
20511 pan.el.addClass('carousel-item-' +lr);
20515 cur.el.on('transitionend', function() {
20516 Roo.log("trans end?");
20518 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20519 pan.setActive(true);
20521 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20522 cur.setActive(false);
20524 _this.transition = false;
20526 }, this, { single: true } );
20531 cur.setActive(false);
20532 pan.setActive(true);
20537 showPanelNext : function()
20539 var i = this.indexOfPanel(this.getActivePanel());
20541 if (i >= this.tabs.length - 1 && !this.autoslide) {
20545 if (i >= this.tabs.length - 1 && this.autoslide) {
20549 this.showPanel(this.tabs[i+1]);
20552 showPanelPrev : function()
20554 var i = this.indexOfPanel(this.getActivePanel());
20556 if (i < 1 && !this.autoslide) {
20560 if (i < 1 && this.autoslide) {
20561 i = this.tabs.length;
20564 this.showPanel(this.tabs[i-1]);
20568 addBullet: function()
20570 if(!this.bullets || Roo.isTouch){
20573 var ctr = this.el.select('.carousel-bullets',true).first();
20574 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20575 var bullet = ctr.createChild({
20576 cls : 'bullet bullet-' + i
20577 },ctr.dom.lastChild);
20582 bullet.on('click', (function(e, el, o, ii, t){
20584 e.preventDefault();
20586 this.showPanel(ii);
20588 if(this.autoslide && this.slideFn){
20589 clearInterval(this.slideFn);
20590 this.slideFn = window.setInterval(function() {
20591 _this.showPanelNext();
20595 }).createDelegate(this, [i, bullet], true));
20600 setActiveBullet : function(i)
20606 Roo.each(this.el.select('.bullet', true).elements, function(el){
20607 el.removeClass('selected');
20610 var bullet = this.el.select('.bullet-' + i, true).first();
20616 bullet.addClass('selected');
20627 Roo.apply(Roo.bootstrap.TabGroup, {
20631 * register a Navigation Group
20632 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20634 register : function(navgrp)
20636 this.groups[navgrp.navId] = navgrp;
20640 * fetch a Navigation Group based on the navigation ID
20641 * if one does not exist , it will get created.
20642 * @param {string} the navgroup to add
20643 * @returns {Roo.bootstrap.NavGroup} the navgroup
20645 get: function(navId) {
20646 if (typeof(this.groups[navId]) == 'undefined') {
20647 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20649 return this.groups[navId] ;
20664 * @class Roo.bootstrap.TabPanel
20665 * @extends Roo.bootstrap.Component
20666 * Bootstrap TabPanel class
20667 * @cfg {Boolean} active panel active
20668 * @cfg {String} html panel content
20669 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20670 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20671 * @cfg {String} href click to link..
20672 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20676 * Create a new TabPanel
20677 * @param {Object} config The config object
20680 Roo.bootstrap.TabPanel = function(config){
20681 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20685 * Fires when the active status changes
20686 * @param {Roo.bootstrap.TabPanel} this
20687 * @param {Boolean} state the new state
20692 * @event beforedeactivate
20693 * Fires before a tab is de-activated - can be used to do validation on a form.
20694 * @param {Roo.bootstrap.TabPanel} this
20695 * @return {Boolean} false if there is an error
20698 'beforedeactivate': true
20701 this.tabId = this.tabId || Roo.id();
20705 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20712 touchSlide : false,
20713 getAutoCreate : function(){
20718 // item is needed for carousel - not sure if it has any effect otherwise
20719 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20720 html: this.html || ''
20724 cfg.cls += ' active';
20728 cfg.tabId = this.tabId;
20736 initEvents: function()
20738 var p = this.parent();
20740 this.navId = this.navId || p.navId;
20742 if (typeof(this.navId) != 'undefined') {
20743 // not really needed.. but just in case.. parent should be a NavGroup.
20744 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20748 var i = tg.tabs.length - 1;
20750 if(this.active && tg.bullets > 0 && i < tg.bullets){
20751 tg.setActiveBullet(i);
20755 this.el.on('click', this.onClick, this);
20757 if(Roo.isTouch && this.touchSlide){
20758 this.el.on("touchstart", this.onTouchStart, this);
20759 this.el.on("touchmove", this.onTouchMove, this);
20760 this.el.on("touchend", this.onTouchEnd, this);
20765 onRender : function(ct, position)
20767 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20770 setActive : function(state)
20772 Roo.log("panel - set active " + this.tabId + "=" + state);
20774 this.active = state;
20776 this.el.removeClass('active');
20778 } else if (!this.el.hasClass('active')) {
20779 this.el.addClass('active');
20782 this.fireEvent('changed', this, state);
20785 onClick : function(e)
20787 e.preventDefault();
20789 if(!this.href.length){
20793 window.location.href = this.href;
20802 onTouchStart : function(e)
20804 this.swiping = false;
20806 this.startX = e.browserEvent.touches[0].clientX;
20807 this.startY = e.browserEvent.touches[0].clientY;
20810 onTouchMove : function(e)
20812 this.swiping = true;
20814 this.endX = e.browserEvent.touches[0].clientX;
20815 this.endY = e.browserEvent.touches[0].clientY;
20818 onTouchEnd : function(e)
20825 var tabGroup = this.parent();
20827 if(this.endX > this.startX){ // swiping right
20828 tabGroup.showPanelPrev();
20832 if(this.startX > this.endX){ // swiping left
20833 tabGroup.showPanelNext();
20852 * @class Roo.bootstrap.DateField
20853 * @extends Roo.bootstrap.Input
20854 * Bootstrap DateField class
20855 * @cfg {Number} weekStart default 0
20856 * @cfg {String} viewMode default empty, (months|years)
20857 * @cfg {String} minViewMode default empty, (months|years)
20858 * @cfg {Number} startDate default -Infinity
20859 * @cfg {Number} endDate default Infinity
20860 * @cfg {Boolean} todayHighlight default false
20861 * @cfg {Boolean} todayBtn default false
20862 * @cfg {Boolean} calendarWeeks default false
20863 * @cfg {Object} daysOfWeekDisabled default empty
20864 * @cfg {Boolean} singleMode default false (true | false)
20866 * @cfg {Boolean} keyboardNavigation default true
20867 * @cfg {String} language default en
20870 * Create a new DateField
20871 * @param {Object} config The config object
20874 Roo.bootstrap.DateField = function(config){
20875 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20879 * Fires when this field show.
20880 * @param {Roo.bootstrap.DateField} this
20881 * @param {Mixed} date The date value
20886 * Fires when this field hide.
20887 * @param {Roo.bootstrap.DateField} this
20888 * @param {Mixed} date The date value
20893 * Fires when select a date.
20894 * @param {Roo.bootstrap.DateField} this
20895 * @param {Mixed} date The date value
20899 * @event beforeselect
20900 * Fires when before select a date.
20901 * @param {Roo.bootstrap.DateField} this
20902 * @param {Mixed} date The date value
20904 beforeselect : true
20908 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20911 * @cfg {String} format
20912 * The default date format string which can be overriden for localization support. The format must be
20913 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20917 * @cfg {String} altFormats
20918 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20919 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20921 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20929 todayHighlight : false,
20935 keyboardNavigation: true,
20937 calendarWeeks: false,
20939 startDate: -Infinity,
20943 daysOfWeekDisabled: [],
20947 singleMode : false,
20949 UTCDate: function()
20951 return new Date(Date.UTC.apply(Date, arguments));
20954 UTCToday: function()
20956 var today = new Date();
20957 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20960 getDate: function() {
20961 var d = this.getUTCDate();
20962 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20965 getUTCDate: function() {
20969 setDate: function(d) {
20970 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20973 setUTCDate: function(d) {
20975 this.setValue(this.formatDate(this.date));
20978 onRender: function(ct, position)
20981 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20983 this.language = this.language || 'en';
20984 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20985 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20987 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20988 this.format = this.format || 'm/d/y';
20989 this.isInline = false;
20990 this.isInput = true;
20991 this.component = this.el.select('.add-on', true).first() || false;
20992 this.component = (this.component && this.component.length === 0) ? false : this.component;
20993 this.hasInput = this.component && this.inputEl().length;
20995 if (typeof(this.minViewMode === 'string')) {
20996 switch (this.minViewMode) {
20998 this.minViewMode = 1;
21001 this.minViewMode = 2;
21004 this.minViewMode = 0;
21009 if (typeof(this.viewMode === 'string')) {
21010 switch (this.viewMode) {
21023 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21025 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21027 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21029 this.picker().on('mousedown', this.onMousedown, this);
21030 this.picker().on('click', this.onClick, this);
21032 this.picker().addClass('datepicker-dropdown');
21034 this.startViewMode = this.viewMode;
21036 if(this.singleMode){
21037 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21038 v.setVisibilityMode(Roo.Element.DISPLAY);
21042 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21043 v.setStyle('width', '189px');
21047 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21048 if(!this.calendarWeeks){
21053 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21054 v.attr('colspan', function(i, val){
21055 return parseInt(val) + 1;
21060 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21062 this.setStartDate(this.startDate);
21063 this.setEndDate(this.endDate);
21065 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21072 if(this.isInline) {
21077 picker : function()
21079 return this.pickerEl;
21080 // return this.el.select('.datepicker', true).first();
21083 fillDow: function()
21085 var dowCnt = this.weekStart;
21094 if(this.calendarWeeks){
21102 while (dowCnt < this.weekStart + 7) {
21106 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21110 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21113 fillMonths: function()
21116 var months = this.picker().select('>.datepicker-months td', true).first();
21118 months.dom.innerHTML = '';
21124 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21127 months.createChild(month);
21134 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;
21136 if (this.date < this.startDate) {
21137 this.viewDate = new Date(this.startDate);
21138 } else if (this.date > this.endDate) {
21139 this.viewDate = new Date(this.endDate);
21141 this.viewDate = new Date(this.date);
21149 var d = new Date(this.viewDate),
21150 year = d.getUTCFullYear(),
21151 month = d.getUTCMonth(),
21152 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21153 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21154 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21155 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21156 currentDate = this.date && this.date.valueOf(),
21157 today = this.UTCToday();
21159 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21161 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21163 // this.picker.select('>tfoot th.today').
21164 // .text(dates[this.language].today)
21165 // .toggle(this.todayBtn !== false);
21167 this.updateNavArrows();
21170 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21172 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21174 prevMonth.setUTCDate(day);
21176 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21178 var nextMonth = new Date(prevMonth);
21180 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21182 nextMonth = nextMonth.valueOf();
21184 var fillMonths = false;
21186 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21188 while(prevMonth.valueOf() <= nextMonth) {
21191 if (prevMonth.getUTCDay() === this.weekStart) {
21193 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21201 if(this.calendarWeeks){
21202 // ISO 8601: First week contains first thursday.
21203 // ISO also states week starts on Monday, but we can be more abstract here.
21205 // Start of current week: based on weekstart/current date
21206 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21207 // Thursday of this week
21208 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21209 // First Thursday of year, year from thursday
21210 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21211 // Calendar week: ms between thursdays, div ms per day, div 7 days
21212 calWeek = (th - yth) / 864e5 / 7 + 1;
21214 fillMonths.cn.push({
21222 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21224 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21227 if (this.todayHighlight &&
21228 prevMonth.getUTCFullYear() == today.getFullYear() &&
21229 prevMonth.getUTCMonth() == today.getMonth() &&
21230 prevMonth.getUTCDate() == today.getDate()) {
21231 clsName += ' today';
21234 if (currentDate && prevMonth.valueOf() === currentDate) {
21235 clsName += ' active';
21238 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21239 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21240 clsName += ' disabled';
21243 fillMonths.cn.push({
21245 cls: 'day ' + clsName,
21246 html: prevMonth.getDate()
21249 prevMonth.setDate(prevMonth.getDate()+1);
21252 var currentYear = this.date && this.date.getUTCFullYear();
21253 var currentMonth = this.date && this.date.getUTCMonth();
21255 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21257 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21258 v.removeClass('active');
21260 if(currentYear === year && k === currentMonth){
21261 v.addClass('active');
21264 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21265 v.addClass('disabled');
21271 year = parseInt(year/10, 10) * 10;
21273 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21275 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21278 for (var i = -1; i < 11; i++) {
21279 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21281 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21289 showMode: function(dir)
21292 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21295 Roo.each(this.picker().select('>div',true).elements, function(v){
21296 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21299 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21304 if(this.isInline) {
21308 this.picker().removeClass(['bottom', 'top']);
21310 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21312 * place to the top of element!
21316 this.picker().addClass('top');
21317 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21322 this.picker().addClass('bottom');
21324 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21327 parseDate : function(value)
21329 if(!value || value instanceof Date){
21332 var v = Date.parseDate(value, this.format);
21333 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21334 v = Date.parseDate(value, 'Y-m-d');
21336 if(!v && this.altFormats){
21337 if(!this.altFormatsArray){
21338 this.altFormatsArray = this.altFormats.split("|");
21340 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21341 v = Date.parseDate(value, this.altFormatsArray[i]);
21347 formatDate : function(date, fmt)
21349 return (!date || !(date instanceof Date)) ?
21350 date : date.dateFormat(fmt || this.format);
21353 onFocus : function()
21355 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21359 onBlur : function()
21361 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21363 var d = this.inputEl().getValue();
21370 showPopup : function()
21372 this.picker().show();
21376 this.fireEvent('showpopup', this, this.date);
21379 hidePopup : function()
21381 if(this.isInline) {
21384 this.picker().hide();
21385 this.viewMode = this.startViewMode;
21388 this.fireEvent('hidepopup', this, this.date);
21392 onMousedown: function(e)
21394 e.stopPropagation();
21395 e.preventDefault();
21400 Roo.bootstrap.DateField.superclass.keyup.call(this);
21404 setValue: function(v)
21406 if(this.fireEvent('beforeselect', this, v) !== false){
21407 var d = new Date(this.parseDate(v) ).clearTime();
21409 if(isNaN(d.getTime())){
21410 this.date = this.viewDate = '';
21411 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21415 v = this.formatDate(d);
21417 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21419 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21423 this.fireEvent('select', this, this.date);
21427 getValue: function()
21429 return this.formatDate(this.date);
21432 fireKey: function(e)
21434 if (!this.picker().isVisible()){
21435 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21441 var dateChanged = false,
21443 newDate, newViewDate;
21448 e.preventDefault();
21452 if (!this.keyboardNavigation) {
21455 dir = e.keyCode == 37 ? -1 : 1;
21458 newDate = this.moveYear(this.date, dir);
21459 newViewDate = this.moveYear(this.viewDate, dir);
21460 } else if (e.shiftKey){
21461 newDate = this.moveMonth(this.date, dir);
21462 newViewDate = this.moveMonth(this.viewDate, dir);
21464 newDate = new Date(this.date);
21465 newDate.setUTCDate(this.date.getUTCDate() + dir);
21466 newViewDate = new Date(this.viewDate);
21467 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21469 if (this.dateWithinRange(newDate)){
21470 this.date = newDate;
21471 this.viewDate = newViewDate;
21472 this.setValue(this.formatDate(this.date));
21474 e.preventDefault();
21475 dateChanged = true;
21480 if (!this.keyboardNavigation) {
21483 dir = e.keyCode == 38 ? -1 : 1;
21485 newDate = this.moveYear(this.date, dir);
21486 newViewDate = this.moveYear(this.viewDate, dir);
21487 } else if (e.shiftKey){
21488 newDate = this.moveMonth(this.date, dir);
21489 newViewDate = this.moveMonth(this.viewDate, dir);
21491 newDate = new Date(this.date);
21492 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21493 newViewDate = new Date(this.viewDate);
21494 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21496 if (this.dateWithinRange(newDate)){
21497 this.date = newDate;
21498 this.viewDate = newViewDate;
21499 this.setValue(this.formatDate(this.date));
21501 e.preventDefault();
21502 dateChanged = true;
21506 this.setValue(this.formatDate(this.date));
21508 e.preventDefault();
21511 this.setValue(this.formatDate(this.date));
21525 onClick: function(e)
21527 e.stopPropagation();
21528 e.preventDefault();
21530 var target = e.getTarget();
21532 if(target.nodeName.toLowerCase() === 'i'){
21533 target = Roo.get(target).dom.parentNode;
21536 var nodeName = target.nodeName;
21537 var className = target.className;
21538 var html = target.innerHTML;
21539 //Roo.log(nodeName);
21541 switch(nodeName.toLowerCase()) {
21543 switch(className) {
21549 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21550 switch(this.viewMode){
21552 this.viewDate = this.moveMonth(this.viewDate, dir);
21556 this.viewDate = this.moveYear(this.viewDate, dir);
21562 var date = new Date();
21563 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21565 this.setValue(this.formatDate(this.date));
21572 if (className.indexOf('disabled') < 0) {
21573 this.viewDate.setUTCDate(1);
21574 if (className.indexOf('month') > -1) {
21575 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21577 var year = parseInt(html, 10) || 0;
21578 this.viewDate.setUTCFullYear(year);
21582 if(this.singleMode){
21583 this.setValue(this.formatDate(this.viewDate));
21594 //Roo.log(className);
21595 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21596 var day = parseInt(html, 10) || 1;
21597 var year = (this.viewDate || new Date()).getUTCFullYear(),
21598 month = (this.viewDate || new Date()).getUTCMonth();
21600 if (className.indexOf('old') > -1) {
21607 } else if (className.indexOf('new') > -1) {
21615 //Roo.log([year,month,day]);
21616 this.date = this.UTCDate(year, month, day,0,0,0,0);
21617 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21619 //Roo.log(this.formatDate(this.date));
21620 this.setValue(this.formatDate(this.date));
21627 setStartDate: function(startDate)
21629 this.startDate = startDate || -Infinity;
21630 if (this.startDate !== -Infinity) {
21631 this.startDate = this.parseDate(this.startDate);
21634 this.updateNavArrows();
21637 setEndDate: function(endDate)
21639 this.endDate = endDate || Infinity;
21640 if (this.endDate !== Infinity) {
21641 this.endDate = this.parseDate(this.endDate);
21644 this.updateNavArrows();
21647 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21649 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21650 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21651 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21653 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21654 return parseInt(d, 10);
21657 this.updateNavArrows();
21660 updateNavArrows: function()
21662 if(this.singleMode){
21666 var d = new Date(this.viewDate),
21667 year = d.getUTCFullYear(),
21668 month = d.getUTCMonth();
21670 Roo.each(this.picker().select('.prev', true).elements, function(v){
21672 switch (this.viewMode) {
21675 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21681 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21688 Roo.each(this.picker().select('.next', true).elements, function(v){
21690 switch (this.viewMode) {
21693 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21699 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21707 moveMonth: function(date, dir)
21712 var new_date = new Date(date.valueOf()),
21713 day = new_date.getUTCDate(),
21714 month = new_date.getUTCMonth(),
21715 mag = Math.abs(dir),
21717 dir = dir > 0 ? 1 : -1;
21720 // If going back one month, make sure month is not current month
21721 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21723 return new_date.getUTCMonth() == month;
21725 // If going forward one month, make sure month is as expected
21726 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21728 return new_date.getUTCMonth() != new_month;
21730 new_month = month + dir;
21731 new_date.setUTCMonth(new_month);
21732 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21733 if (new_month < 0 || new_month > 11) {
21734 new_month = (new_month + 12) % 12;
21737 // For magnitudes >1, move one month at a time...
21738 for (var i=0; i<mag; i++) {
21739 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21740 new_date = this.moveMonth(new_date, dir);
21742 // ...then reset the day, keeping it in the new month
21743 new_month = new_date.getUTCMonth();
21744 new_date.setUTCDate(day);
21746 return new_month != new_date.getUTCMonth();
21749 // Common date-resetting loop -- if date is beyond end of month, make it
21752 new_date.setUTCDate(--day);
21753 new_date.setUTCMonth(new_month);
21758 moveYear: function(date, dir)
21760 return this.moveMonth(date, dir*12);
21763 dateWithinRange: function(date)
21765 return date >= this.startDate && date <= this.endDate;
21771 this.picker().remove();
21774 validateValue : function(value)
21776 if(this.getVisibilityEl().hasClass('hidden')){
21780 if(value.length < 1) {
21781 if(this.allowBlank){
21787 if(value.length < this.minLength){
21790 if(value.length > this.maxLength){
21794 var vt = Roo.form.VTypes;
21795 if(!vt[this.vtype](value, this)){
21799 if(typeof this.validator == "function"){
21800 var msg = this.validator(value);
21806 if(this.regex && !this.regex.test(value)){
21810 if(typeof(this.parseDate(value)) == 'undefined'){
21814 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21818 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21828 this.date = this.viewDate = '';
21830 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21835 Roo.apply(Roo.bootstrap.DateField, {
21846 html: '<i class="fa fa-arrow-left"/>'
21856 html: '<i class="fa fa-arrow-right"/>'
21898 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21899 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21900 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21901 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21902 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21915 navFnc: 'FullYear',
21920 navFnc: 'FullYear',
21925 Roo.apply(Roo.bootstrap.DateField, {
21929 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21933 cls: 'datepicker-days',
21937 cls: 'table-condensed',
21939 Roo.bootstrap.DateField.head,
21943 Roo.bootstrap.DateField.footer
21950 cls: 'datepicker-months',
21954 cls: 'table-condensed',
21956 Roo.bootstrap.DateField.head,
21957 Roo.bootstrap.DateField.content,
21958 Roo.bootstrap.DateField.footer
21965 cls: 'datepicker-years',
21969 cls: 'table-condensed',
21971 Roo.bootstrap.DateField.head,
21972 Roo.bootstrap.DateField.content,
21973 Roo.bootstrap.DateField.footer
21992 * @class Roo.bootstrap.TimeField
21993 * @extends Roo.bootstrap.Input
21994 * Bootstrap DateField class
21998 * Create a new TimeField
21999 * @param {Object} config The config object
22002 Roo.bootstrap.TimeField = function(config){
22003 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22007 * Fires when this field show.
22008 * @param {Roo.bootstrap.DateField} thisthis
22009 * @param {Mixed} date The date value
22014 * Fires when this field hide.
22015 * @param {Roo.bootstrap.DateField} this
22016 * @param {Mixed} date The date value
22021 * Fires when select a date.
22022 * @param {Roo.bootstrap.DateField} this
22023 * @param {Mixed} date The date value
22029 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22032 * @cfg {String} format
22033 * The default time format string which can be overriden for localization support. The format must be
22034 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22038 getAutoCreate : function()
22040 this.after = '<i class="fa far fa-clock"></i>';
22041 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22045 onRender: function(ct, position)
22048 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22050 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22052 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22054 this.pop = this.picker().select('>.datepicker-time',true).first();
22055 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22057 this.picker().on('mousedown', this.onMousedown, this);
22058 this.picker().on('click', this.onClick, this);
22060 this.picker().addClass('datepicker-dropdown');
22065 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22066 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22067 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22068 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22069 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22070 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22074 fireKey: function(e){
22075 if (!this.picker().isVisible()){
22076 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22082 e.preventDefault();
22090 this.onTogglePeriod();
22093 this.onIncrementMinutes();
22096 this.onDecrementMinutes();
22105 onClick: function(e) {
22106 e.stopPropagation();
22107 e.preventDefault();
22110 picker : function()
22112 return this.pickerEl;
22115 fillTime: function()
22117 var time = this.pop.select('tbody', true).first();
22119 time.dom.innerHTML = '';
22134 cls: 'hours-up fa fas fa-chevron-up'
22154 cls: 'minutes-up fa fas fa-chevron-up'
22175 cls: 'timepicker-hour',
22190 cls: 'timepicker-minute',
22205 cls: 'btn btn-primary period',
22227 cls: 'hours-down fa fas fa-chevron-down'
22247 cls: 'minutes-down fa fas fa-chevron-down'
22265 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22272 var hours = this.time.getHours();
22273 var minutes = this.time.getMinutes();
22286 hours = hours - 12;
22290 hours = '0' + hours;
22294 minutes = '0' + minutes;
22297 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22298 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22299 this.pop.select('button', true).first().dom.innerHTML = period;
22305 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22307 var cls = ['bottom'];
22309 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22316 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22320 //this.picker().setXY(20000,20000);
22321 this.picker().addClass(cls.join('-'));
22325 Roo.each(cls, function(c){
22330 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22331 //_this.picker().setTop(_this.inputEl().getHeight());
22335 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22337 //_this.picker().setTop(0 - _this.picker().getHeight());
22342 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22346 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22354 onFocus : function()
22356 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22360 onBlur : function()
22362 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22368 this.picker().show();
22373 this.fireEvent('show', this, this.date);
22378 this.picker().hide();
22381 this.fireEvent('hide', this, this.date);
22384 setTime : function()
22387 this.setValue(this.time.format(this.format));
22389 this.fireEvent('select', this, this.date);
22394 onMousedown: function(e){
22395 e.stopPropagation();
22396 e.preventDefault();
22399 onIncrementHours: function()
22401 Roo.log('onIncrementHours');
22402 this.time = this.time.add(Date.HOUR, 1);
22407 onDecrementHours: function()
22409 Roo.log('onDecrementHours');
22410 this.time = this.time.add(Date.HOUR, -1);
22414 onIncrementMinutes: function()
22416 Roo.log('onIncrementMinutes');
22417 this.time = this.time.add(Date.MINUTE, 1);
22421 onDecrementMinutes: function()
22423 Roo.log('onDecrementMinutes');
22424 this.time = this.time.add(Date.MINUTE, -1);
22428 onTogglePeriod: function()
22430 Roo.log('onTogglePeriod');
22431 this.time = this.time.add(Date.HOUR, 12);
22439 Roo.apply(Roo.bootstrap.TimeField, {
22443 cls: 'datepicker dropdown-menu',
22447 cls: 'datepicker-time',
22451 cls: 'table-condensed',
22480 cls: 'btn btn-info ok',
22508 * @class Roo.bootstrap.MonthField
22509 * @extends Roo.bootstrap.Input
22510 * Bootstrap MonthField class
22512 * @cfg {String} language default en
22515 * Create a new MonthField
22516 * @param {Object} config The config object
22519 Roo.bootstrap.MonthField = function(config){
22520 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22525 * Fires when this field show.
22526 * @param {Roo.bootstrap.MonthField} this
22527 * @param {Mixed} date The date value
22532 * Fires when this field hide.
22533 * @param {Roo.bootstrap.MonthField} this
22534 * @param {Mixed} date The date value
22539 * Fires when select a date.
22540 * @param {Roo.bootstrap.MonthField} this
22541 * @param {String} oldvalue The old value
22542 * @param {String} newvalue The new value
22548 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22550 onRender: function(ct, position)
22553 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22555 this.language = this.language || 'en';
22556 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22557 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22559 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22560 this.isInline = false;
22561 this.isInput = true;
22562 this.component = this.el.select('.add-on', true).first() || false;
22563 this.component = (this.component && this.component.length === 0) ? false : this.component;
22564 this.hasInput = this.component && this.inputEL().length;
22566 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22568 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22570 this.picker().on('mousedown', this.onMousedown, this);
22571 this.picker().on('click', this.onClick, this);
22573 this.picker().addClass('datepicker-dropdown');
22575 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22576 v.setStyle('width', '189px');
22583 if(this.isInline) {
22589 setValue: function(v, suppressEvent)
22591 var o = this.getValue();
22593 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22597 if(suppressEvent !== true){
22598 this.fireEvent('select', this, o, v);
22603 getValue: function()
22608 onClick: function(e)
22610 e.stopPropagation();
22611 e.preventDefault();
22613 var target = e.getTarget();
22615 if(target.nodeName.toLowerCase() === 'i'){
22616 target = Roo.get(target).dom.parentNode;
22619 var nodeName = target.nodeName;
22620 var className = target.className;
22621 var html = target.innerHTML;
22623 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22627 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22629 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22635 picker : function()
22637 return this.pickerEl;
22640 fillMonths: function()
22643 var months = this.picker().select('>.datepicker-months td', true).first();
22645 months.dom.innerHTML = '';
22651 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22654 months.createChild(month);
22663 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22664 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22667 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22668 e.removeClass('active');
22670 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22671 e.addClass('active');
22678 if(this.isInline) {
22682 this.picker().removeClass(['bottom', 'top']);
22684 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22686 * place to the top of element!
22690 this.picker().addClass('top');
22691 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22696 this.picker().addClass('bottom');
22698 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22701 onFocus : function()
22703 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22707 onBlur : function()
22709 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22711 var d = this.inputEl().getValue();
22720 this.picker().show();
22721 this.picker().select('>.datepicker-months', true).first().show();
22725 this.fireEvent('show', this, this.date);
22730 if(this.isInline) {
22733 this.picker().hide();
22734 this.fireEvent('hide', this, this.date);
22738 onMousedown: function(e)
22740 e.stopPropagation();
22741 e.preventDefault();
22746 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22750 fireKey: function(e)
22752 if (!this.picker().isVisible()){
22753 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22764 e.preventDefault();
22768 dir = e.keyCode == 37 ? -1 : 1;
22770 this.vIndex = this.vIndex + dir;
22772 if(this.vIndex < 0){
22776 if(this.vIndex > 11){
22780 if(isNaN(this.vIndex)){
22784 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22790 dir = e.keyCode == 38 ? -1 : 1;
22792 this.vIndex = this.vIndex + dir * 4;
22794 if(this.vIndex < 0){
22798 if(this.vIndex > 11){
22802 if(isNaN(this.vIndex)){
22806 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22811 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22812 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22816 e.preventDefault();
22819 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22820 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22836 this.picker().remove();
22841 Roo.apply(Roo.bootstrap.MonthField, {
22860 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22861 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22866 Roo.apply(Roo.bootstrap.MonthField, {
22870 cls: 'datepicker dropdown-menu roo-dynamic',
22874 cls: 'datepicker-months',
22878 cls: 'table-condensed',
22880 Roo.bootstrap.DateField.content
22900 * @class Roo.bootstrap.CheckBox
22901 * @extends Roo.bootstrap.Input
22902 * Bootstrap CheckBox class
22904 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22905 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22906 * @cfg {String} boxLabel The text that appears beside the checkbox
22907 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22908 * @cfg {Boolean} checked initnal the element
22909 * @cfg {Boolean} inline inline the element (default false)
22910 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22911 * @cfg {String} tooltip label tooltip
22914 * Create a new CheckBox
22915 * @param {Object} config The config object
22918 Roo.bootstrap.CheckBox = function(config){
22919 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22924 * Fires when the element is checked or unchecked.
22925 * @param {Roo.bootstrap.CheckBox} this This input
22926 * @param {Boolean} checked The new checked value
22931 * Fires when the element is click.
22932 * @param {Roo.bootstrap.CheckBox} this This input
22939 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22941 inputType: 'checkbox',
22950 // checkbox success does not make any sense really..
22955 getAutoCreate : function()
22957 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22963 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22966 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22972 type : this.inputType,
22973 value : this.inputValue,
22974 cls : 'roo-' + this.inputType, //'form-box',
22975 placeholder : this.placeholder || ''
22979 if(this.inputType != 'radio'){
22983 cls : 'roo-hidden-value',
22984 value : this.checked ? this.inputValue : this.valueOff
22989 if (this.weight) { // Validity check?
22990 cfg.cls += " " + this.inputType + "-" + this.weight;
22993 if (this.disabled) {
22994 input.disabled=true;
22998 input.checked = this.checked;
23003 input.name = this.name;
23005 if(this.inputType != 'radio'){
23006 hidden.name = this.name;
23007 input.name = '_hidden_' + this.name;
23012 input.cls += ' input-' + this.size;
23017 ['xs','sm','md','lg'].map(function(size){
23018 if (settings[size]) {
23019 cfg.cls += ' col-' + size + '-' + settings[size];
23023 var inputblock = input;
23025 if (this.before || this.after) {
23028 cls : 'input-group',
23033 inputblock.cn.push({
23035 cls : 'input-group-addon',
23040 inputblock.cn.push(input);
23042 if(this.inputType != 'radio'){
23043 inputblock.cn.push(hidden);
23047 inputblock.cn.push({
23049 cls : 'input-group-addon',
23055 var boxLabelCfg = false;
23061 //'for': id, // box label is handled by onclick - so no for...
23063 html: this.boxLabel
23066 boxLabelCfg.tooltip = this.tooltip;
23072 if (align ==='left' && this.fieldLabel.length) {
23073 // Roo.log("left and has label");
23078 cls : 'control-label',
23079 html : this.fieldLabel
23090 cfg.cn[1].cn.push(boxLabelCfg);
23093 if(this.labelWidth > 12){
23094 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23097 if(this.labelWidth < 13 && this.labelmd == 0){
23098 this.labelmd = this.labelWidth;
23101 if(this.labellg > 0){
23102 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23103 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23106 if(this.labelmd > 0){
23107 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23108 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23111 if(this.labelsm > 0){
23112 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23113 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23116 if(this.labelxs > 0){
23117 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23118 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23121 } else if ( this.fieldLabel.length) {
23122 // Roo.log(" label");
23126 tag: this.boxLabel ? 'span' : 'label',
23128 cls: 'control-label box-input-label',
23129 //cls : 'input-group-addon',
23130 html : this.fieldLabel
23137 cfg.cn.push(boxLabelCfg);
23142 // Roo.log(" no label && no align");
23143 cfg.cn = [ inputblock ] ;
23145 cfg.cn.push(boxLabelCfg);
23153 if(this.inputType != 'radio'){
23154 cfg.cn.push(hidden);
23162 * return the real input element.
23164 inputEl: function ()
23166 return this.el.select('input.roo-' + this.inputType,true).first();
23168 hiddenEl: function ()
23170 return this.el.select('input.roo-hidden-value',true).first();
23173 labelEl: function()
23175 return this.el.select('label.control-label',true).first();
23177 /* depricated... */
23181 return this.labelEl();
23184 boxLabelEl: function()
23186 return this.el.select('label.box-label',true).first();
23189 initEvents : function()
23191 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23193 this.inputEl().on('click', this.onClick, this);
23195 if (this.boxLabel) {
23196 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23199 this.startValue = this.getValue();
23202 Roo.bootstrap.CheckBox.register(this);
23206 onClick : function(e)
23208 if(this.fireEvent('click', this, e) !== false){
23209 this.setChecked(!this.checked);
23214 setChecked : function(state,suppressEvent)
23216 this.startValue = this.getValue();
23218 if(this.inputType == 'radio'){
23220 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23221 e.dom.checked = false;
23224 this.inputEl().dom.checked = true;
23226 this.inputEl().dom.value = this.inputValue;
23228 if(suppressEvent !== true){
23229 this.fireEvent('check', this, true);
23237 this.checked = state;
23239 this.inputEl().dom.checked = state;
23242 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23244 if(suppressEvent !== true){
23245 this.fireEvent('check', this, state);
23251 getValue : function()
23253 if(this.inputType == 'radio'){
23254 return this.getGroupValue();
23257 return this.hiddenEl().dom.value;
23261 getGroupValue : function()
23263 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23267 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23270 setValue : function(v,suppressEvent)
23272 if(this.inputType == 'radio'){
23273 this.setGroupValue(v, suppressEvent);
23277 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23282 setGroupValue : function(v, suppressEvent)
23284 this.startValue = this.getValue();
23286 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23287 e.dom.checked = false;
23289 if(e.dom.value == v){
23290 e.dom.checked = true;
23294 if(suppressEvent !== true){
23295 this.fireEvent('check', this, true);
23303 validate : function()
23305 if(this.getVisibilityEl().hasClass('hidden')){
23311 (this.inputType == 'radio' && this.validateRadio()) ||
23312 (this.inputType == 'checkbox' && this.validateCheckbox())
23318 this.markInvalid();
23322 validateRadio : function()
23324 if(this.getVisibilityEl().hasClass('hidden')){
23328 if(this.allowBlank){
23334 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23335 if(!e.dom.checked){
23347 validateCheckbox : function()
23350 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23351 //return (this.getValue() == this.inputValue) ? true : false;
23354 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23362 for(var i in group){
23363 if(group[i].el.isVisible(true)){
23371 for(var i in group){
23376 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23383 * Mark this field as valid
23385 markValid : function()
23389 this.fireEvent('valid', this);
23391 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23394 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23401 if(this.inputType == 'radio'){
23402 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23403 var fg = e.findParent('.form-group', false, true);
23404 if (Roo.bootstrap.version == 3) {
23405 fg.removeClass([_this.invalidClass, _this.validClass]);
23406 fg.addClass(_this.validClass);
23408 fg.removeClass(['is-valid', 'is-invalid']);
23409 fg.addClass('is-valid');
23417 var fg = this.el.findParent('.form-group', false, true);
23418 if (Roo.bootstrap.version == 3) {
23419 fg.removeClass([this.invalidClass, this.validClass]);
23420 fg.addClass(this.validClass);
23422 fg.removeClass(['is-valid', 'is-invalid']);
23423 fg.addClass('is-valid');
23428 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23434 for(var i in group){
23435 var fg = group[i].el.findParent('.form-group', false, true);
23436 if (Roo.bootstrap.version == 3) {
23437 fg.removeClass([this.invalidClass, this.validClass]);
23438 fg.addClass(this.validClass);
23440 fg.removeClass(['is-valid', 'is-invalid']);
23441 fg.addClass('is-valid');
23447 * Mark this field as invalid
23448 * @param {String} msg The validation message
23450 markInvalid : function(msg)
23452 if(this.allowBlank){
23458 this.fireEvent('invalid', this, msg);
23460 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23463 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23467 label.markInvalid();
23470 if(this.inputType == 'radio'){
23472 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23473 var fg = e.findParent('.form-group', false, true);
23474 if (Roo.bootstrap.version == 3) {
23475 fg.removeClass([_this.invalidClass, _this.validClass]);
23476 fg.addClass(_this.invalidClass);
23478 fg.removeClass(['is-invalid', 'is-valid']);
23479 fg.addClass('is-invalid');
23487 var fg = this.el.findParent('.form-group', false, true);
23488 if (Roo.bootstrap.version == 3) {
23489 fg.removeClass([_this.invalidClass, _this.validClass]);
23490 fg.addClass(_this.invalidClass);
23492 fg.removeClass(['is-invalid', 'is-valid']);
23493 fg.addClass('is-invalid');
23498 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23504 for(var i in group){
23505 var fg = group[i].el.findParent('.form-group', false, true);
23506 if (Roo.bootstrap.version == 3) {
23507 fg.removeClass([_this.invalidClass, _this.validClass]);
23508 fg.addClass(_this.invalidClass);
23510 fg.removeClass(['is-invalid', 'is-valid']);
23511 fg.addClass('is-invalid');
23517 clearInvalid : function()
23519 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23521 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23523 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23525 if (label && label.iconEl) {
23526 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23527 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23531 disable : function()
23533 if(this.inputType != 'radio'){
23534 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23541 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23542 _this.getActionEl().addClass(this.disabledClass);
23543 e.dom.disabled = true;
23547 this.disabled = true;
23548 this.fireEvent("disable", this);
23552 enable : function()
23554 if(this.inputType != 'radio'){
23555 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23562 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23563 _this.getActionEl().removeClass(this.disabledClass);
23564 e.dom.disabled = false;
23568 this.disabled = false;
23569 this.fireEvent("enable", this);
23573 setBoxLabel : function(v)
23578 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23584 Roo.apply(Roo.bootstrap.CheckBox, {
23589 * register a CheckBox Group
23590 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23592 register : function(checkbox)
23594 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23595 this.groups[checkbox.groupId] = {};
23598 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23602 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23606 * fetch a CheckBox Group based on the group ID
23607 * @param {string} the group ID
23608 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23610 get: function(groupId) {
23611 if (typeof(this.groups[groupId]) == 'undefined') {
23615 return this.groups[groupId] ;
23628 * @class Roo.bootstrap.Radio
23629 * @extends Roo.bootstrap.Component
23630 * Bootstrap Radio class
23631 * @cfg {String} boxLabel - the label associated
23632 * @cfg {String} value - the value of radio
23635 * Create a new Radio
23636 * @param {Object} config The config object
23638 Roo.bootstrap.Radio = function(config){
23639 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23643 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23649 getAutoCreate : function()
23653 cls : 'form-group radio',
23658 html : this.boxLabel
23666 initEvents : function()
23668 this.parent().register(this);
23670 this.el.on('click', this.onClick, this);
23674 onClick : function(e)
23676 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23677 this.setChecked(true);
23681 setChecked : function(state, suppressEvent)
23683 this.parent().setValue(this.value, suppressEvent);
23687 setBoxLabel : function(v)
23692 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23707 * @class Roo.bootstrap.SecurePass
23708 * @extends Roo.bootstrap.Input
23709 * Bootstrap SecurePass class
23713 * Create a new SecurePass
23714 * @param {Object} config The config object
23717 Roo.bootstrap.SecurePass = function (config) {
23718 // these go here, so the translation tool can replace them..
23720 PwdEmpty: "Please type a password, and then retype it to confirm.",
23721 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23722 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23723 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23724 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23725 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23726 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23727 TooWeak: "Your password is Too Weak."
23729 this.meterLabel = "Password strength:";
23730 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23731 this.meterClass = [
23732 "roo-password-meter-tooweak",
23733 "roo-password-meter-weak",
23734 "roo-password-meter-medium",
23735 "roo-password-meter-strong",
23736 "roo-password-meter-grey"
23741 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23744 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23746 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23748 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23749 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23750 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23751 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23752 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23753 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23754 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23764 * @cfg {String/Object} Label for the strength meter (defaults to
23765 * 'Password strength:')
23770 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23771 * ['Weak', 'Medium', 'Strong'])
23774 pwdStrengths: false,
23787 initEvents: function ()
23789 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23791 if (this.el.is('input[type=password]') && Roo.isSafari) {
23792 this.el.on('keydown', this.SafariOnKeyDown, this);
23795 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23798 onRender: function (ct, position)
23800 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23801 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23802 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23804 this.trigger.createChild({
23809 cls: 'roo-password-meter-grey col-xs-12',
23812 //width: this.meterWidth + 'px'
23816 cls: 'roo-password-meter-text'
23822 if (this.hideTrigger) {
23823 this.trigger.setDisplayed(false);
23825 this.setSize(this.width || '', this.height || '');
23828 onDestroy: function ()
23830 if (this.trigger) {
23831 this.trigger.removeAllListeners();
23832 this.trigger.remove();
23835 this.wrap.remove();
23837 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23840 checkStrength: function ()
23842 var pwd = this.inputEl().getValue();
23843 if (pwd == this._lastPwd) {
23848 if (this.ClientSideStrongPassword(pwd)) {
23850 } else if (this.ClientSideMediumPassword(pwd)) {
23852 } else if (this.ClientSideWeakPassword(pwd)) {
23858 Roo.log('strength1: ' + strength);
23860 //var pm = this.trigger.child('div/div/div').dom;
23861 var pm = this.trigger.child('div/div');
23862 pm.removeClass(this.meterClass);
23863 pm.addClass(this.meterClass[strength]);
23866 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23868 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23870 this._lastPwd = pwd;
23874 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23876 this._lastPwd = '';
23878 var pm = this.trigger.child('div/div');
23879 pm.removeClass(this.meterClass);
23880 pm.addClass('roo-password-meter-grey');
23883 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23886 this.inputEl().dom.type='password';
23889 validateValue: function (value)
23891 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23894 if (value.length == 0) {
23895 if (this.allowBlank) {
23896 this.clearInvalid();
23900 this.markInvalid(this.errors.PwdEmpty);
23901 this.errorMsg = this.errors.PwdEmpty;
23909 if (!value.match(/[\x21-\x7e]+/)) {
23910 this.markInvalid(this.errors.PwdBadChar);
23911 this.errorMsg = this.errors.PwdBadChar;
23914 if (value.length < 6) {
23915 this.markInvalid(this.errors.PwdShort);
23916 this.errorMsg = this.errors.PwdShort;
23919 if (value.length > 16) {
23920 this.markInvalid(this.errors.PwdLong);
23921 this.errorMsg = this.errors.PwdLong;
23925 if (this.ClientSideStrongPassword(value)) {
23927 } else if (this.ClientSideMediumPassword(value)) {
23929 } else if (this.ClientSideWeakPassword(value)) {
23936 if (strength < 2) {
23937 //this.markInvalid(this.errors.TooWeak);
23938 this.errorMsg = this.errors.TooWeak;
23943 console.log('strength2: ' + strength);
23945 //var pm = this.trigger.child('div/div/div').dom;
23947 var pm = this.trigger.child('div/div');
23948 pm.removeClass(this.meterClass);
23949 pm.addClass(this.meterClass[strength]);
23951 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23953 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23955 this.errorMsg = '';
23959 CharacterSetChecks: function (type)
23962 this.fResult = false;
23965 isctype: function (character, type)
23968 case this.kCapitalLetter:
23969 if (character >= 'A' && character <= 'Z') {
23974 case this.kSmallLetter:
23975 if (character >= 'a' && character <= 'z') {
23981 if (character >= '0' && character <= '9') {
23986 case this.kPunctuation:
23987 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23998 IsLongEnough: function (pwd, size)
24000 return !(pwd == null || isNaN(size) || pwd.length < size);
24003 SpansEnoughCharacterSets: function (word, nb)
24005 if (!this.IsLongEnough(word, nb))
24010 var characterSetChecks = new Array(
24011 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24012 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24015 for (var index = 0; index < word.length; ++index) {
24016 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24017 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24018 characterSetChecks[nCharSet].fResult = true;
24025 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24026 if (characterSetChecks[nCharSet].fResult) {
24031 if (nCharSets < nb) {
24037 ClientSideStrongPassword: function (pwd)
24039 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24042 ClientSideMediumPassword: function (pwd)
24044 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24047 ClientSideWeakPassword: function (pwd)
24049 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24052 })//<script type="text/javascript">
24055 * Based Ext JS Library 1.1.1
24056 * Copyright(c) 2006-2007, Ext JS, LLC.
24062 * @class Roo.HtmlEditorCore
24063 * @extends Roo.Component
24064 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24066 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24069 Roo.HtmlEditorCore = function(config){
24072 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24077 * @event initialize
24078 * Fires when the editor is fully initialized (including the iframe)
24079 * @param {Roo.HtmlEditorCore} this
24084 * Fires when the editor is first receives the focus. Any insertion must wait
24085 * until after this event.
24086 * @param {Roo.HtmlEditorCore} this
24090 * @event beforesync
24091 * Fires before the textarea is updated with content from the editor iframe. Return false
24092 * to cancel the sync.
24093 * @param {Roo.HtmlEditorCore} this
24094 * @param {String} html
24098 * @event beforepush
24099 * Fires before the iframe editor is updated with content from the textarea. Return false
24100 * to cancel the push.
24101 * @param {Roo.HtmlEditorCore} this
24102 * @param {String} html
24107 * Fires when the textarea is updated with content from the editor iframe.
24108 * @param {Roo.HtmlEditorCore} this
24109 * @param {String} html
24114 * Fires when the iframe editor is updated with content from the textarea.
24115 * @param {Roo.HtmlEditorCore} this
24116 * @param {String} html
24121 * @event editorevent
24122 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24123 * @param {Roo.HtmlEditorCore} this
24129 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24131 // defaults : white / black...
24132 this.applyBlacklists();
24139 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24143 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24149 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24154 * @cfg {Number} height (in pixels)
24158 * @cfg {Number} width (in pixels)
24163 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24166 stylesheets: false,
24171 // private properties
24172 validationEvent : false,
24174 initialized : false,
24176 sourceEditMode : false,
24177 onFocus : Roo.emptyFn,
24179 hideMode:'offsets',
24183 // blacklist + whitelisted elements..
24190 * Protected method that will not generally be called directly. It
24191 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24192 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24194 getDocMarkup : function(){
24198 // inherit styels from page...??
24199 if (this.stylesheets === false) {
24201 Roo.get(document.head).select('style').each(function(node) {
24202 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24205 Roo.get(document.head).select('link').each(function(node) {
24206 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24209 } else if (!this.stylesheets.length) {
24211 st = '<style type="text/css">' +
24212 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24215 for (var i in this.stylesheets) {
24216 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24221 st += '<style type="text/css">' +
24222 'IMG { cursor: pointer } ' +
24225 var cls = 'roo-htmleditor-body';
24227 if(this.bodyCls.length){
24228 cls += ' ' + this.bodyCls;
24231 return '<html><head>' + st +
24232 //<style type="text/css">' +
24233 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24235 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24239 onRender : function(ct, position)
24242 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24243 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24246 this.el.dom.style.border = '0 none';
24247 this.el.dom.setAttribute('tabIndex', -1);
24248 this.el.addClass('x-hidden hide');
24252 if(Roo.isIE){ // fix IE 1px bogus margin
24253 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24257 this.frameId = Roo.id();
24261 var iframe = this.owner.wrap.createChild({
24263 cls: 'form-control', // bootstrap..
24265 name: this.frameId,
24266 frameBorder : 'no',
24267 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24272 this.iframe = iframe.dom;
24274 this.assignDocWin();
24276 this.doc.designMode = 'on';
24279 this.doc.write(this.getDocMarkup());
24283 var task = { // must defer to wait for browser to be ready
24285 //console.log("run task?" + this.doc.readyState);
24286 this.assignDocWin();
24287 if(this.doc.body || this.doc.readyState == 'complete'){
24289 this.doc.designMode="on";
24293 Roo.TaskMgr.stop(task);
24294 this.initEditor.defer(10, this);
24301 Roo.TaskMgr.start(task);
24306 onResize : function(w, h)
24308 Roo.log('resize: ' +w + ',' + h );
24309 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24313 if(typeof w == 'number'){
24315 this.iframe.style.width = w + 'px';
24317 if(typeof h == 'number'){
24319 this.iframe.style.height = h + 'px';
24321 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24328 * Toggles the editor between standard and source edit mode.
24329 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24331 toggleSourceEdit : function(sourceEditMode){
24333 this.sourceEditMode = sourceEditMode === true;
24335 if(this.sourceEditMode){
24337 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24340 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24341 //this.iframe.className = '';
24344 //this.setSize(this.owner.wrap.getSize());
24345 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24352 * Protected method that will not generally be called directly. If you need/want
24353 * custom HTML cleanup, this is the method you should override.
24354 * @param {String} html The HTML to be cleaned
24355 * return {String} The cleaned HTML
24357 cleanHtml : function(html){
24358 html = String(html);
24359 if(html.length > 5){
24360 if(Roo.isSafari){ // strip safari nonsense
24361 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24364 if(html == ' '){
24371 * HTML Editor -> Textarea
24372 * Protected method that will not generally be called directly. Syncs the contents
24373 * of the editor iframe with the textarea.
24375 syncValue : function(){
24376 if(this.initialized){
24377 var bd = (this.doc.body || this.doc.documentElement);
24378 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24379 var html = bd.innerHTML;
24381 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24382 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24384 html = '<div style="'+m[0]+'">' + html + '</div>';
24387 html = this.cleanHtml(html);
24388 // fix up the special chars.. normaly like back quotes in word...
24389 // however we do not want to do this with chinese..
24390 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24392 var cc = match.charCodeAt();
24394 // Get the character value, handling surrogate pairs
24395 if (match.length == 2) {
24396 // It's a surrogate pair, calculate the Unicode code point
24397 var high = match.charCodeAt(0) - 0xD800;
24398 var low = match.charCodeAt(1) - 0xDC00;
24399 cc = (high * 0x400) + low + 0x10000;
24401 (cc >= 0x4E00 && cc < 0xA000 ) ||
24402 (cc >= 0x3400 && cc < 0x4E00 ) ||
24403 (cc >= 0xf900 && cc < 0xfb00 )
24408 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24409 return "&#" + cc + ";";
24416 if(this.owner.fireEvent('beforesync', this, html) !== false){
24417 this.el.dom.value = html;
24418 this.owner.fireEvent('sync', this, html);
24424 * Protected method that will not generally be called directly. Pushes the value of the textarea
24425 * into the iframe editor.
24427 pushValue : function(){
24428 if(this.initialized){
24429 var v = this.el.dom.value.trim();
24431 // if(v.length < 1){
24435 if(this.owner.fireEvent('beforepush', this, v) !== false){
24436 var d = (this.doc.body || this.doc.documentElement);
24438 this.cleanUpPaste();
24439 this.el.dom.value = d.innerHTML;
24440 this.owner.fireEvent('push', this, v);
24446 deferFocus : function(){
24447 this.focus.defer(10, this);
24451 focus : function(){
24452 if(this.win && !this.sourceEditMode){
24459 assignDocWin: function()
24461 var iframe = this.iframe;
24464 this.doc = iframe.contentWindow.document;
24465 this.win = iframe.contentWindow;
24467 // if (!Roo.get(this.frameId)) {
24470 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24471 // this.win = Roo.get(this.frameId).dom.contentWindow;
24473 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24477 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24478 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24483 initEditor : function(){
24484 //console.log("INIT EDITOR");
24485 this.assignDocWin();
24489 this.doc.designMode="on";
24491 this.doc.write(this.getDocMarkup());
24494 var dbody = (this.doc.body || this.doc.documentElement);
24495 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24496 // this copies styles from the containing element into thsi one..
24497 // not sure why we need all of this..
24498 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24500 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24501 //ss['background-attachment'] = 'fixed'; // w3c
24502 dbody.bgProperties = 'fixed'; // ie
24503 //Roo.DomHelper.applyStyles(dbody, ss);
24504 Roo.EventManager.on(this.doc, {
24505 //'mousedown': this.onEditorEvent,
24506 'mouseup': this.onEditorEvent,
24507 'dblclick': this.onEditorEvent,
24508 'click': this.onEditorEvent,
24509 'keyup': this.onEditorEvent,
24514 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24516 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24517 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24519 this.initialized = true;
24521 this.owner.fireEvent('initialize', this);
24526 onDestroy : function(){
24532 //for (var i =0; i < this.toolbars.length;i++) {
24533 // // fixme - ask toolbars for heights?
24534 // this.toolbars[i].onDestroy();
24537 //this.wrap.dom.innerHTML = '';
24538 //this.wrap.remove();
24543 onFirstFocus : function(){
24545 this.assignDocWin();
24548 this.activated = true;
24551 if(Roo.isGecko){ // prevent silly gecko errors
24553 var s = this.win.getSelection();
24554 if(!s.focusNode || s.focusNode.nodeType != 3){
24555 var r = s.getRangeAt(0);
24556 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24561 this.execCmd('useCSS', true);
24562 this.execCmd('styleWithCSS', false);
24565 this.owner.fireEvent('activate', this);
24569 adjustFont: function(btn){
24570 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24571 //if(Roo.isSafari){ // safari
24574 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24575 if(Roo.isSafari){ // safari
24576 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24577 v = (v < 10) ? 10 : v;
24578 v = (v > 48) ? 48 : v;
24579 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24584 v = Math.max(1, v+adjust);
24586 this.execCmd('FontSize', v );
24589 onEditorEvent : function(e)
24591 this.owner.fireEvent('editorevent', this, e);
24592 // this.updateToolbar();
24593 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24596 insertTag : function(tg)
24598 // could be a bit smarter... -> wrap the current selected tRoo..
24599 if (tg.toLowerCase() == 'span' ||
24600 tg.toLowerCase() == 'code' ||
24601 tg.toLowerCase() == 'sup' ||
24602 tg.toLowerCase() == 'sub'
24605 range = this.createRange(this.getSelection());
24606 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24607 wrappingNode.appendChild(range.extractContents());
24608 range.insertNode(wrappingNode);
24615 this.execCmd("formatblock", tg);
24619 insertText : function(txt)
24623 var range = this.createRange();
24624 range.deleteContents();
24625 //alert(Sender.getAttribute('label'));
24627 range.insertNode(this.doc.createTextNode(txt));
24633 * Executes a Midas editor command on the editor document and performs necessary focus and
24634 * toolbar updates. <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 relayCmd : function(cmd, value){
24640 this.execCmd(cmd, value);
24641 this.owner.fireEvent('editorevent', this);
24642 //this.updateToolbar();
24643 this.owner.deferFocus();
24647 * Executes a Midas editor command directly on the editor document.
24648 * For visual commands, you should use {@link #relayCmd} instead.
24649 * <b>This should only be called after the editor is initialized.</b>
24650 * @param {String} cmd The Midas command
24651 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24653 execCmd : function(cmd, value){
24654 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24661 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24663 * @param {String} text | dom node..
24665 insertAtCursor : function(text)
24668 if(!this.activated){
24674 var r = this.doc.selection.createRange();
24685 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24689 // from jquery ui (MIT licenced)
24691 var win = this.win;
24693 if (win.getSelection && win.getSelection().getRangeAt) {
24694 range = win.getSelection().getRangeAt(0);
24695 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24696 range.insertNode(node);
24697 } else if (win.document.selection && win.document.selection.createRange) {
24698 // no firefox support
24699 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24700 win.document.selection.createRange().pasteHTML(txt);
24702 // no firefox support
24703 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24704 this.execCmd('InsertHTML', txt);
24713 mozKeyPress : function(e){
24715 var c = e.getCharCode(), cmd;
24718 c = String.fromCharCode(c).toLowerCase();
24732 this.cleanUpPaste.defer(100, this);
24740 e.preventDefault();
24748 fixKeys : function(){ // load time branching for fastest keydown performance
24750 return function(e){
24751 var k = e.getKey(), r;
24754 r = this.doc.selection.createRange();
24757 r.pasteHTML('    ');
24764 r = this.doc.selection.createRange();
24766 var target = r.parentElement();
24767 if(!target || target.tagName.toLowerCase() != 'li'){
24769 r.pasteHTML('<br />');
24775 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24776 this.cleanUpPaste.defer(100, this);
24782 }else if(Roo.isOpera){
24783 return function(e){
24784 var k = e.getKey();
24788 this.execCmd('InsertHTML','    ');
24791 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24792 this.cleanUpPaste.defer(100, this);
24797 }else if(Roo.isSafari){
24798 return function(e){
24799 var k = e.getKey();
24803 this.execCmd('InsertText','\t');
24807 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24808 this.cleanUpPaste.defer(100, this);
24816 getAllAncestors: function()
24818 var p = this.getSelectedNode();
24821 a.push(p); // push blank onto stack..
24822 p = this.getParentElement();
24826 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24830 a.push(this.doc.body);
24834 lastSelNode : false,
24837 getSelection : function()
24839 this.assignDocWin();
24840 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24843 getSelectedNode: function()
24845 // this may only work on Gecko!!!
24847 // should we cache this!!!!
24852 var range = this.createRange(this.getSelection()).cloneRange();
24855 var parent = range.parentElement();
24857 var testRange = range.duplicate();
24858 testRange.moveToElementText(parent);
24859 if (testRange.inRange(range)) {
24862 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24865 parent = parent.parentElement;
24870 // is ancestor a text element.
24871 var ac = range.commonAncestorContainer;
24872 if (ac.nodeType == 3) {
24873 ac = ac.parentNode;
24876 var ar = ac.childNodes;
24879 var other_nodes = [];
24880 var has_other_nodes = false;
24881 for (var i=0;i<ar.length;i++) {
24882 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24885 // fullly contained node.
24887 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24892 // probably selected..
24893 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24894 other_nodes.push(ar[i]);
24898 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24903 has_other_nodes = true;
24905 if (!nodes.length && other_nodes.length) {
24906 nodes= other_nodes;
24908 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24914 createRange: function(sel)
24916 // this has strange effects when using with
24917 // top toolbar - not sure if it's a great idea.
24918 //this.editor.contentWindow.focus();
24919 if (typeof sel != "undefined") {
24921 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24923 return this.doc.createRange();
24926 return this.doc.createRange();
24929 getParentElement: function()
24932 this.assignDocWin();
24933 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24935 var range = this.createRange(sel);
24938 var p = range.commonAncestorContainer;
24939 while (p.nodeType == 3) { // text node
24950 * Range intersection.. the hard stuff...
24954 * [ -- selected range --- ]
24958 * if end is before start or hits it. fail.
24959 * if start is after end or hits it fail.
24961 * if either hits (but other is outside. - then it's not
24967 // @see http://www.thismuchiknow.co.uk/?p=64.
24968 rangeIntersectsNode : function(range, node)
24970 var nodeRange = node.ownerDocument.createRange();
24972 nodeRange.selectNode(node);
24974 nodeRange.selectNodeContents(node);
24977 var rangeStartRange = range.cloneRange();
24978 rangeStartRange.collapse(true);
24980 var rangeEndRange = range.cloneRange();
24981 rangeEndRange.collapse(false);
24983 var nodeStartRange = nodeRange.cloneRange();
24984 nodeStartRange.collapse(true);
24986 var nodeEndRange = nodeRange.cloneRange();
24987 nodeEndRange.collapse(false);
24989 return rangeStartRange.compareBoundaryPoints(
24990 Range.START_TO_START, nodeEndRange) == -1 &&
24991 rangeEndRange.compareBoundaryPoints(
24992 Range.START_TO_START, nodeStartRange) == 1;
24996 rangeCompareNode : function(range, node)
24998 var nodeRange = node.ownerDocument.createRange();
25000 nodeRange.selectNode(node);
25002 nodeRange.selectNodeContents(node);
25006 range.collapse(true);
25008 nodeRange.collapse(true);
25010 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25011 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25013 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25015 var nodeIsBefore = ss == 1;
25016 var nodeIsAfter = ee == -1;
25018 if (nodeIsBefore && nodeIsAfter) {
25021 if (!nodeIsBefore && nodeIsAfter) {
25022 return 1; //right trailed.
25025 if (nodeIsBefore && !nodeIsAfter) {
25026 return 2; // left trailed.
25032 // private? - in a new class?
25033 cleanUpPaste : function()
25035 // cleans up the whole document..
25036 Roo.log('cleanuppaste');
25038 this.cleanUpChildren(this.doc.body);
25039 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25040 if (clean != this.doc.body.innerHTML) {
25041 this.doc.body.innerHTML = clean;
25046 cleanWordChars : function(input) {// change the chars to hex code
25047 var he = Roo.HtmlEditorCore;
25049 var output = input;
25050 Roo.each(he.swapCodes, function(sw) {
25051 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25053 output = output.replace(swapper, sw[1]);
25060 cleanUpChildren : function (n)
25062 if (!n.childNodes.length) {
25065 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25066 this.cleanUpChild(n.childNodes[i]);
25073 cleanUpChild : function (node)
25076 //console.log(node);
25077 if (node.nodeName == "#text") {
25078 // clean up silly Windows -- stuff?
25081 if (node.nodeName == "#comment") {
25082 node.parentNode.removeChild(node);
25083 // clean up silly Windows -- stuff?
25086 var lcname = node.tagName.toLowerCase();
25087 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25088 // whitelist of tags..
25090 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25092 node.parentNode.removeChild(node);
25097 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25099 // spans with no attributes - just remove them..
25100 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25101 remove_keep_children = true;
25104 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25105 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25107 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25108 // remove_keep_children = true;
25111 if (remove_keep_children) {
25112 this.cleanUpChildren(node);
25113 // inserts everything just before this node...
25114 while (node.childNodes.length) {
25115 var cn = node.childNodes[0];
25116 node.removeChild(cn);
25117 node.parentNode.insertBefore(cn, node);
25119 node.parentNode.removeChild(node);
25123 if (!node.attributes || !node.attributes.length) {
25128 this.cleanUpChildren(node);
25132 function cleanAttr(n,v)
25135 if (v.match(/^\./) || v.match(/^\//)) {
25138 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25141 if (v.match(/^#/)) {
25144 if (v.match(/^\{/)) { // allow template editing.
25147 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25148 node.removeAttribute(n);
25152 var cwhite = this.cwhite;
25153 var cblack = this.cblack;
25155 function cleanStyle(n,v)
25157 if (v.match(/expression/)) { //XSS?? should we even bother..
25158 node.removeAttribute(n);
25162 var parts = v.split(/;/);
25165 Roo.each(parts, function(p) {
25166 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25170 var l = p.split(':').shift().replace(/\s+/g,'');
25171 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25173 if ( cwhite.length && cblack.indexOf(l) > -1) {
25174 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25175 //node.removeAttribute(n);
25179 // only allow 'c whitelisted system attributes'
25180 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25181 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25182 //node.removeAttribute(n);
25192 if (clean.length) {
25193 node.setAttribute(n, clean.join(';'));
25195 node.removeAttribute(n);
25201 for (var i = node.attributes.length-1; i > -1 ; i--) {
25202 var a = node.attributes[i];
25205 if (a.name.toLowerCase().substr(0,2)=='on') {
25206 node.removeAttribute(a.name);
25209 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25210 node.removeAttribute(a.name);
25213 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25214 cleanAttr(a.name,a.value); // fixme..
25217 if (a.name == 'style') {
25218 cleanStyle(a.name,a.value);
25221 /// clean up MS crap..
25222 // tecnically this should be a list of valid class'es..
25225 if (a.name == 'class') {
25226 if (a.value.match(/^Mso/)) {
25227 node.removeAttribute('class');
25230 if (a.value.match(/^body$/)) {
25231 node.removeAttribute('class');
25242 this.cleanUpChildren(node);
25248 * Clean up MS wordisms...
25250 cleanWord : function(node)
25253 this.cleanWord(this.doc.body);
25258 node.nodeName == 'SPAN' &&
25259 !node.hasAttributes() &&
25260 node.childNodes.length == 1 &&
25261 node.firstChild.nodeName == "#text"
25263 var textNode = node.firstChild;
25264 node.removeChild(textNode);
25265 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25266 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25268 node.parentNode.insertBefore(textNode, node);
25269 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25270 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25272 node.parentNode.removeChild(node);
25275 if (node.nodeName == "#text") {
25276 // clean up silly Windows -- stuff?
25279 if (node.nodeName == "#comment") {
25280 node.parentNode.removeChild(node);
25281 // clean up silly Windows -- stuff?
25285 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25286 node.parentNode.removeChild(node);
25289 //Roo.log(node.tagName);
25290 // remove - but keep children..
25291 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25292 //Roo.log('-- removed');
25293 while (node.childNodes.length) {
25294 var cn = node.childNodes[0];
25295 node.removeChild(cn);
25296 node.parentNode.insertBefore(cn, node);
25297 // move node to parent - and clean it..
25298 this.cleanWord(cn);
25300 node.parentNode.removeChild(node);
25301 /// no need to iterate chidlren = it's got none..
25302 //this.iterateChildren(node, this.cleanWord);
25306 if (node.className.length) {
25308 var cn = node.className.split(/\W+/);
25310 Roo.each(cn, function(cls) {
25311 if (cls.match(/Mso[a-zA-Z]+/)) {
25316 node.className = cna.length ? cna.join(' ') : '';
25318 node.removeAttribute("class");
25322 if (node.hasAttribute("lang")) {
25323 node.removeAttribute("lang");
25326 if (node.hasAttribute("style")) {
25328 var styles = node.getAttribute("style").split(";");
25330 Roo.each(styles, function(s) {
25331 if (!s.match(/:/)) {
25334 var kv = s.split(":");
25335 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25338 // what ever is left... we allow.
25341 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25342 if (!nstyle.length) {
25343 node.removeAttribute('style');
25346 this.iterateChildren(node, this.cleanWord);
25352 * iterateChildren of a Node, calling fn each time, using this as the scole..
25353 * @param {DomNode} node node to iterate children of.
25354 * @param {Function} fn method of this class to call on each item.
25356 iterateChildren : function(node, fn)
25358 if (!node.childNodes.length) {
25361 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25362 fn.call(this, node.childNodes[i])
25368 * cleanTableWidths.
25370 * Quite often pasting from word etc.. results in tables with column and widths.
25371 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25374 cleanTableWidths : function(node)
25379 this.cleanTableWidths(this.doc.body);
25384 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25387 Roo.log(node.tagName);
25388 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25389 this.iterateChildren(node, this.cleanTableWidths);
25392 if (node.hasAttribute('width')) {
25393 node.removeAttribute('width');
25397 if (node.hasAttribute("style")) {
25400 var styles = node.getAttribute("style").split(";");
25402 Roo.each(styles, function(s) {
25403 if (!s.match(/:/)) {
25406 var kv = s.split(":");
25407 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25410 // what ever is left... we allow.
25413 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25414 if (!nstyle.length) {
25415 node.removeAttribute('style');
25419 this.iterateChildren(node, this.cleanTableWidths);
25427 domToHTML : function(currentElement, depth, nopadtext) {
25429 depth = depth || 0;
25430 nopadtext = nopadtext || false;
25432 if (!currentElement) {
25433 return this.domToHTML(this.doc.body);
25436 //Roo.log(currentElement);
25438 var allText = false;
25439 var nodeName = currentElement.nodeName;
25440 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25442 if (nodeName == '#text') {
25444 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25449 if (nodeName != 'BODY') {
25452 // Prints the node tagName, such as <A>, <IMG>, etc
25455 for(i = 0; i < currentElement.attributes.length;i++) {
25457 var aname = currentElement.attributes.item(i).name;
25458 if (!currentElement.attributes.item(i).value.length) {
25461 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25464 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25473 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25476 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25481 // Traverse the tree
25483 var currentElementChild = currentElement.childNodes.item(i);
25484 var allText = true;
25485 var innerHTML = '';
25487 while (currentElementChild) {
25488 // Formatting code (indent the tree so it looks nice on the screen)
25489 var nopad = nopadtext;
25490 if (lastnode == 'SPAN') {
25494 if (currentElementChild.nodeName == '#text') {
25495 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25496 toadd = nopadtext ? toadd : toadd.trim();
25497 if (!nopad && toadd.length > 80) {
25498 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25500 innerHTML += toadd;
25503 currentElementChild = currentElement.childNodes.item(i);
25509 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25511 // Recursively traverse the tree structure of the child node
25512 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25513 lastnode = currentElementChild.nodeName;
25515 currentElementChild=currentElement.childNodes.item(i);
25521 // The remaining code is mostly for formatting the tree
25522 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25527 ret+= "</"+tagName+">";
25533 applyBlacklists : function()
25535 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25536 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25540 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25541 if (b.indexOf(tag) > -1) {
25544 this.white.push(tag);
25548 Roo.each(w, function(tag) {
25549 if (b.indexOf(tag) > -1) {
25552 if (this.white.indexOf(tag) > -1) {
25555 this.white.push(tag);
25560 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25561 if (w.indexOf(tag) > -1) {
25564 this.black.push(tag);
25568 Roo.each(b, function(tag) {
25569 if (w.indexOf(tag) > -1) {
25572 if (this.black.indexOf(tag) > -1) {
25575 this.black.push(tag);
25580 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25581 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25585 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25586 if (b.indexOf(tag) > -1) {
25589 this.cwhite.push(tag);
25593 Roo.each(w, function(tag) {
25594 if (b.indexOf(tag) > -1) {
25597 if (this.cwhite.indexOf(tag) > -1) {
25600 this.cwhite.push(tag);
25605 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25606 if (w.indexOf(tag) > -1) {
25609 this.cblack.push(tag);
25613 Roo.each(b, function(tag) {
25614 if (w.indexOf(tag) > -1) {
25617 if (this.cblack.indexOf(tag) > -1) {
25620 this.cblack.push(tag);
25625 setStylesheets : function(stylesheets)
25627 if(typeof(stylesheets) == 'string'){
25628 Roo.get(this.iframe.contentDocument.head).createChild({
25630 rel : 'stylesheet',
25639 Roo.each(stylesheets, function(s) {
25644 Roo.get(_this.iframe.contentDocument.head).createChild({
25646 rel : 'stylesheet',
25655 removeStylesheets : function()
25659 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25664 setStyle : function(style)
25666 Roo.get(this.iframe.contentDocument.head).createChild({
25675 // hide stuff that is not compatible
25689 * @event specialkey
25693 * @cfg {String} fieldClass @hide
25696 * @cfg {String} focusClass @hide
25699 * @cfg {String} autoCreate @hide
25702 * @cfg {String} inputType @hide
25705 * @cfg {String} invalidClass @hide
25708 * @cfg {String} invalidText @hide
25711 * @cfg {String} msgFx @hide
25714 * @cfg {String} validateOnBlur @hide
25718 Roo.HtmlEditorCore.white = [
25719 'area', 'br', 'img', 'input', 'hr', 'wbr',
25721 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25722 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25723 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25724 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25725 'table', 'ul', 'xmp',
25727 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25730 'dir', 'menu', 'ol', 'ul', 'dl',
25736 Roo.HtmlEditorCore.black = [
25737 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25739 'base', 'basefont', 'bgsound', 'blink', 'body',
25740 'frame', 'frameset', 'head', 'html', 'ilayer',
25741 'iframe', 'layer', 'link', 'meta', 'object',
25742 'script', 'style' ,'title', 'xml' // clean later..
25744 Roo.HtmlEditorCore.clean = [
25745 'script', 'style', 'title', 'xml'
25747 Roo.HtmlEditorCore.remove = [
25752 Roo.HtmlEditorCore.ablack = [
25756 Roo.HtmlEditorCore.aclean = [
25757 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25761 Roo.HtmlEditorCore.pwhite= [
25762 'http', 'https', 'mailto'
25765 // white listed style attributes.
25766 Roo.HtmlEditorCore.cwhite= [
25767 // 'text-align', /// default is to allow most things..
25773 // black listed style attributes.
25774 Roo.HtmlEditorCore.cblack= [
25775 // 'font-size' -- this can be set by the project
25779 Roo.HtmlEditorCore.swapCodes =[
25798 * @class Roo.bootstrap.HtmlEditor
25799 * @extends Roo.bootstrap.TextArea
25800 * Bootstrap HtmlEditor class
25803 * Create a new HtmlEditor
25804 * @param {Object} config The config object
25807 Roo.bootstrap.HtmlEditor = function(config){
25808 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25809 if (!this.toolbars) {
25810 this.toolbars = [];
25813 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25816 * @event initialize
25817 * Fires when the editor is fully initialized (including the iframe)
25818 * @param {HtmlEditor} this
25823 * Fires when the editor is first receives the focus. Any insertion must wait
25824 * until after this event.
25825 * @param {HtmlEditor} this
25829 * @event beforesync
25830 * Fires before the textarea is updated with content from the editor iframe. Return false
25831 * to cancel the sync.
25832 * @param {HtmlEditor} this
25833 * @param {String} html
25837 * @event beforepush
25838 * Fires before the iframe editor is updated with content from the textarea. Return false
25839 * to cancel the push.
25840 * @param {HtmlEditor} this
25841 * @param {String} html
25846 * Fires when the textarea is updated with content from the editor iframe.
25847 * @param {HtmlEditor} this
25848 * @param {String} html
25853 * Fires when the iframe editor is updated with content from the textarea.
25854 * @param {HtmlEditor} this
25855 * @param {String} html
25859 * @event editmodechange
25860 * Fires when the editor switches edit modes
25861 * @param {HtmlEditor} this
25862 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25864 editmodechange: true,
25866 * @event editorevent
25867 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25868 * @param {HtmlEditor} this
25872 * @event firstfocus
25873 * Fires when on first focus - needed by toolbars..
25874 * @param {HtmlEditor} this
25879 * Auto save the htmlEditor value as a file into Events
25880 * @param {HtmlEditor} this
25884 * @event savedpreview
25885 * preview the saved version of htmlEditor
25886 * @param {HtmlEditor} this
25893 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25897 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25902 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25907 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25912 * @cfg {Number} height (in pixels)
25916 * @cfg {Number} width (in pixels)
25921 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25924 stylesheets: false,
25929 // private properties
25930 validationEvent : false,
25932 initialized : false,
25935 onFocus : Roo.emptyFn,
25937 hideMode:'offsets',
25939 tbContainer : false,
25943 toolbarContainer :function() {
25944 return this.wrap.select('.x-html-editor-tb',true).first();
25948 * Protected method that will not generally be called directly. It
25949 * is called when the editor creates its toolbar. Override this method if you need to
25950 * add custom toolbar buttons.
25951 * @param {HtmlEditor} editor
25953 createToolbar : function(){
25954 Roo.log('renewing');
25955 Roo.log("create toolbars");
25957 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25958 this.toolbars[0].render(this.toolbarContainer());
25962 // if (!editor.toolbars || !editor.toolbars.length) {
25963 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25966 // for (var i =0 ; i < editor.toolbars.length;i++) {
25967 // editor.toolbars[i] = Roo.factory(
25968 // typeof(editor.toolbars[i]) == 'string' ?
25969 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25970 // Roo.bootstrap.HtmlEditor);
25971 // editor.toolbars[i].init(editor);
25977 onRender : function(ct, position)
25979 // Roo.log("Call onRender: " + this.xtype);
25981 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25983 this.wrap = this.inputEl().wrap({
25984 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25987 this.editorcore.onRender(ct, position);
25989 if (this.resizable) {
25990 this.resizeEl = new Roo.Resizable(this.wrap, {
25994 minHeight : this.height,
25995 height: this.height,
25996 handles : this.resizable,
25999 resize : function(r, w, h) {
26000 _t.onResize(w,h); // -something
26006 this.createToolbar(this);
26009 if(!this.width && this.resizable){
26010 this.setSize(this.wrap.getSize());
26012 if (this.resizeEl) {
26013 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26014 // should trigger onReize..
26020 onResize : function(w, h)
26022 Roo.log('resize: ' +w + ',' + h );
26023 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26027 if(this.inputEl() ){
26028 if(typeof w == 'number'){
26029 var aw = w - this.wrap.getFrameWidth('lr');
26030 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26033 if(typeof h == 'number'){
26034 var tbh = -11; // fixme it needs to tool bar size!
26035 for (var i =0; i < this.toolbars.length;i++) {
26036 // fixme - ask toolbars for heights?
26037 tbh += this.toolbars[i].el.getHeight();
26038 //if (this.toolbars[i].footer) {
26039 // tbh += this.toolbars[i].footer.el.getHeight();
26047 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26048 ah -= 5; // knock a few pixes off for look..
26049 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26053 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26054 this.editorcore.onResize(ew,eh);
26059 * Toggles the editor between standard and source edit mode.
26060 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26062 toggleSourceEdit : function(sourceEditMode)
26064 this.editorcore.toggleSourceEdit(sourceEditMode);
26066 if(this.editorcore.sourceEditMode){
26067 Roo.log('editor - showing textarea');
26070 // Roo.log(this.syncValue());
26072 this.inputEl().removeClass(['hide', 'x-hidden']);
26073 this.inputEl().dom.removeAttribute('tabIndex');
26074 this.inputEl().focus();
26076 Roo.log('editor - hiding textarea');
26078 // Roo.log(this.pushValue());
26081 this.inputEl().addClass(['hide', 'x-hidden']);
26082 this.inputEl().dom.setAttribute('tabIndex', -1);
26083 //this.deferFocus();
26086 if(this.resizable){
26087 this.setSize(this.wrap.getSize());
26090 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26093 // private (for BoxComponent)
26094 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26096 // private (for BoxComponent)
26097 getResizeEl : function(){
26101 // private (for BoxComponent)
26102 getPositionEl : function(){
26107 initEvents : function(){
26108 this.originalValue = this.getValue();
26112 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26115 // markInvalid : Roo.emptyFn,
26117 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26120 // clearInvalid : Roo.emptyFn,
26122 setValue : function(v){
26123 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26124 this.editorcore.pushValue();
26129 deferFocus : function(){
26130 this.focus.defer(10, this);
26134 focus : function(){
26135 this.editorcore.focus();
26141 onDestroy : function(){
26147 for (var i =0; i < this.toolbars.length;i++) {
26148 // fixme - ask toolbars for heights?
26149 this.toolbars[i].onDestroy();
26152 this.wrap.dom.innerHTML = '';
26153 this.wrap.remove();
26158 onFirstFocus : function(){
26159 //Roo.log("onFirstFocus");
26160 this.editorcore.onFirstFocus();
26161 for (var i =0; i < this.toolbars.length;i++) {
26162 this.toolbars[i].onFirstFocus();
26168 syncValue : function()
26170 this.editorcore.syncValue();
26173 pushValue : function()
26175 this.editorcore.pushValue();
26179 // hide stuff that is not compatible
26193 * @event specialkey
26197 * @cfg {String} fieldClass @hide
26200 * @cfg {String} focusClass @hide
26203 * @cfg {String} autoCreate @hide
26206 * @cfg {String} inputType @hide
26210 * @cfg {String} invalidText @hide
26213 * @cfg {String} msgFx @hide
26216 * @cfg {String} validateOnBlur @hide
26225 Roo.namespace('Roo.bootstrap.htmleditor');
26227 * @class Roo.bootstrap.HtmlEditorToolbar1
26233 new Roo.bootstrap.HtmlEditor({
26236 new Roo.bootstrap.HtmlEditorToolbar1({
26237 disable : { fonts: 1 , format: 1, ..., ... , ...],
26243 * @cfg {Object} disable List of elements to disable..
26244 * @cfg {Array} btns List of additional buttons.
26248 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26251 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26254 Roo.apply(this, config);
26256 // default disabled, based on 'good practice'..
26257 this.disable = this.disable || {};
26258 Roo.applyIf(this.disable, {
26261 specialElements : true
26263 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26265 this.editor = config.editor;
26266 this.editorcore = config.editor.editorcore;
26268 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26270 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26271 // dont call parent... till later.
26273 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26278 editorcore : false,
26283 "h1","h2","h3","h4","h5","h6",
26285 "abbr", "acronym", "address", "cite", "samp", "var",
26289 onRender : function(ct, position)
26291 // Roo.log("Call onRender: " + this.xtype);
26293 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26295 this.el.dom.style.marginBottom = '0';
26297 var editorcore = this.editorcore;
26298 var editor= this.editor;
26301 var btn = function(id,cmd , toggle, handler, html){
26303 var event = toggle ? 'toggle' : 'click';
26308 xns: Roo.bootstrap,
26312 enableToggle:toggle !== false,
26314 pressed : toggle ? false : null,
26317 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26318 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26324 // var cb_box = function...
26329 xns: Roo.bootstrap,
26334 xns: Roo.bootstrap,
26338 Roo.each(this.formats, function(f) {
26339 style.menu.items.push({
26341 xns: Roo.bootstrap,
26342 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26347 editorcore.insertTag(this.tagname);
26354 children.push(style);
26356 btn('bold',false,true);
26357 btn('italic',false,true);
26358 btn('align-left', 'justifyleft',true);
26359 btn('align-center', 'justifycenter',true);
26360 btn('align-right' , 'justifyright',true);
26361 btn('link', false, false, function(btn) {
26362 //Roo.log("create link?");
26363 var url = prompt(this.createLinkText, this.defaultLinkValue);
26364 if(url && url != 'http:/'+'/'){
26365 this.editorcore.relayCmd('createlink', url);
26368 btn('list','insertunorderedlist',true);
26369 btn('pencil', false,true, function(btn){
26371 this.toggleSourceEdit(btn.pressed);
26374 if (this.editor.btns.length > 0) {
26375 for (var i = 0; i<this.editor.btns.length; i++) {
26376 children.push(this.editor.btns[i]);
26384 xns: Roo.bootstrap,
26389 xns: Roo.bootstrap,
26394 cog.menu.items.push({
26396 xns: Roo.bootstrap,
26397 html : Clean styles,
26402 editorcore.insertTag(this.tagname);
26411 this.xtype = 'NavSimplebar';
26413 for(var i=0;i< children.length;i++) {
26415 this.buttons.add(this.addxtypeChild(children[i]));
26419 editor.on('editorevent', this.updateToolbar, this);
26421 onBtnClick : function(id)
26423 this.editorcore.relayCmd(id);
26424 this.editorcore.focus();
26428 * Protected method that will not generally be called directly. It triggers
26429 * a toolbar update by reading the markup state of the current selection in the editor.
26431 updateToolbar: function(){
26433 if(!this.editorcore.activated){
26434 this.editor.onFirstFocus(); // is this neeed?
26438 var btns = this.buttons;
26439 var doc = this.editorcore.doc;
26440 btns.get('bold').setActive(doc.queryCommandState('bold'));
26441 btns.get('italic').setActive(doc.queryCommandState('italic'));
26442 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26444 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26445 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26446 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26448 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26449 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26452 var ans = this.editorcore.getAllAncestors();
26453 if (this.formatCombo) {
26456 var store = this.formatCombo.store;
26457 this.formatCombo.setValue("");
26458 for (var i =0; i < ans.length;i++) {
26459 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26461 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26469 // hides menus... - so this cant be on a menu...
26470 Roo.bootstrap.MenuMgr.hideAll();
26472 Roo.bootstrap.MenuMgr.hideAll();
26473 //this.editorsyncValue();
26475 onFirstFocus: function() {
26476 this.buttons.each(function(item){
26480 toggleSourceEdit : function(sourceEditMode){
26483 if(sourceEditMode){
26484 Roo.log("disabling buttons");
26485 this.buttons.each( function(item){
26486 if(item.cmd != 'pencil'){
26492 Roo.log("enabling buttons");
26493 if(this.editorcore.initialized){
26494 this.buttons.each( function(item){
26500 Roo.log("calling toggole on editor");
26501 // tell the editor that it's been pressed..
26502 this.editor.toggleSourceEdit(sourceEditMode);
26516 * @class Roo.bootstrap.Markdown
26517 * @extends Roo.bootstrap.TextArea
26518 * Bootstrap Showdown editable area
26519 * @cfg {string} content
26522 * Create a new Showdown
26525 Roo.bootstrap.Markdown = function(config){
26526 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26530 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26534 initEvents : function()
26537 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26538 this.markdownEl = this.el.createChild({
26539 cls : 'roo-markdown-area'
26541 this.inputEl().addClass('d-none');
26542 if (this.getValue() == '') {
26543 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26546 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26548 this.markdownEl.on('click', this.toggleTextEdit, this);
26549 this.on('blur', this.toggleTextEdit, this);
26550 this.on('specialkey', this.resizeTextArea, this);
26553 toggleTextEdit : function()
26555 var sh = this.markdownEl.getHeight();
26556 this.inputEl().addClass('d-none');
26557 this.markdownEl.addClass('d-none');
26558 if (!this.editing) {
26560 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26561 this.inputEl().removeClass('d-none');
26562 this.inputEl().focus();
26563 this.editing = true;
26566 // show showdown...
26567 this.updateMarkdown();
26568 this.markdownEl.removeClass('d-none');
26569 this.editing = false;
26572 updateMarkdown : function()
26574 if (this.getValue() == '') {
26575 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26579 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26582 resizeTextArea: function () {
26585 Roo.log([sh, this.getValue().split("\n").length * 30]);
26586 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26588 setValue : function(val)
26590 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26591 if (!this.editing) {
26592 this.updateMarkdown();
26598 if (!this.editing) {
26599 this.toggleTextEdit();
26607 * @class Roo.bootstrap.Table.AbstractSelectionModel
26608 * @extends Roo.util.Observable
26609 * Abstract base class for grid SelectionModels. It provides the interface that should be
26610 * implemented by descendant classes. This class should not be directly instantiated.
26613 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26614 this.locked = false;
26615 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26619 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26620 /** @ignore Called by the grid automatically. Do not call directly. */
26621 init : function(grid){
26627 * Locks the selections.
26630 this.locked = true;
26634 * Unlocks the selections.
26636 unlock : function(){
26637 this.locked = false;
26641 * Returns true if the selections are locked.
26642 * @return {Boolean}
26644 isLocked : function(){
26645 return this.locked;
26649 initEvents : function ()
26655 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26656 * @class Roo.bootstrap.Table.RowSelectionModel
26657 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26658 * It supports multiple selections and keyboard selection/navigation.
26660 * @param {Object} config
26663 Roo.bootstrap.Table.RowSelectionModel = function(config){
26664 Roo.apply(this, config);
26665 this.selections = new Roo.util.MixedCollection(false, function(o){
26670 this.lastActive = false;
26674 * @event selectionchange
26675 * Fires when the selection changes
26676 * @param {SelectionModel} this
26678 "selectionchange" : true,
26680 * @event afterselectionchange
26681 * Fires after the selection changes (eg. by key press or clicking)
26682 * @param {SelectionModel} this
26684 "afterselectionchange" : true,
26686 * @event beforerowselect
26687 * Fires when a row is selected being selected, return false to cancel.
26688 * @param {SelectionModel} this
26689 * @param {Number} rowIndex The selected index
26690 * @param {Boolean} keepExisting False if other selections will be cleared
26692 "beforerowselect" : true,
26695 * Fires when a row is selected.
26696 * @param {SelectionModel} this
26697 * @param {Number} rowIndex The selected index
26698 * @param {Roo.data.Record} r The record
26700 "rowselect" : true,
26702 * @event rowdeselect
26703 * Fires when a row is deselected.
26704 * @param {SelectionModel} this
26705 * @param {Number} rowIndex The selected index
26707 "rowdeselect" : true
26709 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26710 this.locked = false;
26713 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26715 * @cfg {Boolean} singleSelect
26716 * True to allow selection of only one row at a time (defaults to false)
26718 singleSelect : false,
26721 initEvents : function()
26724 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26725 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26726 //}else{ // allow click to work like normal
26727 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26729 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26730 this.grid.on("rowclick", this.handleMouseDown, this);
26732 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26733 "up" : function(e){
26735 this.selectPrevious(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);
26748 "down" : function(e){
26750 this.selectNext(e.shiftKey);
26751 }else if(this.last !== false && this.lastActive !== false){
26752 var last = this.last;
26753 this.selectRange(this.last, this.lastActive+1);
26754 this.grid.getView().focusRow(this.lastActive);
26755 if(last !== false){
26759 this.selectFirstRow();
26761 this.fireEvent("afterselectionchange", this);
26765 this.grid.store.on('load', function(){
26766 this.selections.clear();
26769 var view = this.grid.view;
26770 view.on("refresh", this.onRefresh, this);
26771 view.on("rowupdated", this.onRowUpdated, this);
26772 view.on("rowremoved", this.onRemove, this);
26777 onRefresh : function()
26779 var ds = this.grid.store, i, v = this.grid.view;
26780 var s = this.selections;
26781 s.each(function(r){
26782 if((i = ds.indexOfId(r.id)) != -1){
26791 onRemove : function(v, index, r){
26792 this.selections.remove(r);
26796 onRowUpdated : function(v, index, r){
26797 if(this.isSelected(r)){
26798 v.onRowSelect(index);
26804 * @param {Array} records The records to select
26805 * @param {Boolean} keepExisting (optional) True to keep existing selections
26807 selectRecords : function(records, keepExisting)
26810 this.clearSelections();
26812 var ds = this.grid.store;
26813 for(var i = 0, len = records.length; i < len; i++){
26814 this.selectRow(ds.indexOf(records[i]), true);
26819 * Gets the number of selected rows.
26822 getCount : function(){
26823 return this.selections.length;
26827 * Selects the first row in the grid.
26829 selectFirstRow : function(){
26834 * Select the last row.
26835 * @param {Boolean} keepExisting (optional) True to keep existing selections
26837 selectLastRow : function(keepExisting){
26838 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26839 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26843 * Selects the row immediately following the last selected row.
26844 * @param {Boolean} keepExisting (optional) True to keep existing selections
26846 selectNext : function(keepExisting)
26848 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26849 this.selectRow(this.last+1, keepExisting);
26850 this.grid.getView().focusRow(this.last);
26855 * Selects the row that precedes the last selected row.
26856 * @param {Boolean} keepExisting (optional) True to keep existing selections
26858 selectPrevious : function(keepExisting){
26860 this.selectRow(this.last-1, keepExisting);
26861 this.grid.getView().focusRow(this.last);
26866 * Returns the selected records
26867 * @return {Array} Array of selected records
26869 getSelections : function(){
26870 return [].concat(this.selections.items);
26874 * Returns the first selected record.
26877 getSelected : function(){
26878 return this.selections.itemAt(0);
26883 * Clears all selections.
26885 clearSelections : function(fast)
26891 var ds = this.grid.store;
26892 var s = this.selections;
26893 s.each(function(r){
26894 this.deselectRow(ds.indexOfId(r.id));
26898 this.selections.clear();
26905 * Selects all rows.
26907 selectAll : function(){
26911 this.selections.clear();
26912 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26913 this.selectRow(i, true);
26918 * Returns True if there is a selection.
26919 * @return {Boolean}
26921 hasSelection : function(){
26922 return this.selections.length > 0;
26926 * Returns True if the specified row is selected.
26927 * @param {Number/Record} record The record or index of the record to check
26928 * @return {Boolean}
26930 isSelected : function(index){
26931 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26932 return (r && this.selections.key(r.id) ? true : false);
26936 * Returns True if the specified record id is selected.
26937 * @param {String} id The id of record to check
26938 * @return {Boolean}
26940 isIdSelected : function(id){
26941 return (this.selections.key(id) ? true : false);
26946 handleMouseDBClick : function(e, t){
26950 handleMouseDown : function(e, t)
26952 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26953 if(this.isLocked() || rowIndex < 0 ){
26956 if(e.shiftKey && this.last !== false){
26957 var last = this.last;
26958 this.selectRange(last, rowIndex, e.ctrlKey);
26959 this.last = last; // reset the last
26963 var isSelected = this.isSelected(rowIndex);
26964 //Roo.log("select row:" + rowIndex);
26966 this.deselectRow(rowIndex);
26968 this.selectRow(rowIndex, true);
26972 if(e.button !== 0 && isSelected){
26973 alert('rowIndex 2: ' + rowIndex);
26974 view.focusRow(rowIndex);
26975 }else if(e.ctrlKey && isSelected){
26976 this.deselectRow(rowIndex);
26977 }else if(!isSelected){
26978 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26979 view.focusRow(rowIndex);
26983 this.fireEvent("afterselectionchange", this);
26986 handleDragableRowClick : function(grid, rowIndex, e)
26988 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26989 this.selectRow(rowIndex, false);
26990 grid.view.focusRow(rowIndex);
26991 this.fireEvent("afterselectionchange", this);
26996 * Selects multiple rows.
26997 * @param {Array} rows Array of the indexes of the row to select
26998 * @param {Boolean} keepExisting (optional) True to keep existing selections
27000 selectRows : function(rows, keepExisting){
27002 this.clearSelections();
27004 for(var i = 0, len = rows.length; i < len; i++){
27005 this.selectRow(rows[i], true);
27010 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27011 * @param {Number} startRow The index of the first row in the range
27012 * @param {Number} endRow The index of the last row in the range
27013 * @param {Boolean} keepExisting (optional) True to retain existing selections
27015 selectRange : function(startRow, endRow, keepExisting){
27020 this.clearSelections();
27022 if(startRow <= endRow){
27023 for(var i = startRow; i <= endRow; i++){
27024 this.selectRow(i, true);
27027 for(var i = startRow; i >= endRow; i--){
27028 this.selectRow(i, true);
27034 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27035 * @param {Number} startRow The index of the first row in the range
27036 * @param {Number} endRow The index of the last row in the range
27038 deselectRange : function(startRow, endRow, preventViewNotify){
27042 for(var i = startRow; i <= endRow; i++){
27043 this.deselectRow(i, preventViewNotify);
27049 * @param {Number} row The index of the row to select
27050 * @param {Boolean} keepExisting (optional) True to keep existing selections
27052 selectRow : function(index, keepExisting, preventViewNotify)
27054 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27057 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27058 if(!keepExisting || this.singleSelect){
27059 this.clearSelections();
27062 var r = this.grid.store.getAt(index);
27063 //console.log('selectRow - record id :' + r.id);
27065 this.selections.add(r);
27066 this.last = this.lastActive = index;
27067 if(!preventViewNotify){
27068 var proxy = new Roo.Element(
27069 this.grid.getRowDom(index)
27071 proxy.addClass('bg-info info');
27073 this.fireEvent("rowselect", this, index, r);
27074 this.fireEvent("selectionchange", this);
27080 * @param {Number} row The index of the row to deselect
27082 deselectRow : function(index, preventViewNotify)
27087 if(this.last == index){
27090 if(this.lastActive == index){
27091 this.lastActive = false;
27094 var r = this.grid.store.getAt(index);
27099 this.selections.remove(r);
27100 //.console.log('deselectRow - record id :' + r.id);
27101 if(!preventViewNotify){
27103 var proxy = new Roo.Element(
27104 this.grid.getRowDom(index)
27106 proxy.removeClass('bg-info info');
27108 this.fireEvent("rowdeselect", this, index);
27109 this.fireEvent("selectionchange", this);
27113 restoreLast : function(){
27115 this.last = this._last;
27120 acceptsNav : function(row, col, cm){
27121 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27125 onEditorKey : function(field, e){
27126 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27131 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27133 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27135 }else if(k == e.ENTER && !e.ctrlKey){
27139 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27141 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27143 }else if(k == e.ESC){
27147 g.startEditing(newCell[0], newCell[1]);
27153 * Ext JS Library 1.1.1
27154 * Copyright(c) 2006-2007, Ext JS, LLC.
27156 * Originally Released Under LGPL - original licence link has changed is not relivant.
27159 * <script type="text/javascript">
27163 * @class Roo.bootstrap.PagingToolbar
27164 * @extends Roo.bootstrap.NavSimplebar
27165 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27167 * Create a new PagingToolbar
27168 * @param {Object} config The config object
27169 * @param {Roo.data.Store} store
27171 Roo.bootstrap.PagingToolbar = function(config)
27173 // old args format still supported... - xtype is prefered..
27174 // created from xtype...
27176 this.ds = config.dataSource;
27178 if (config.store && !this.ds) {
27179 this.store= Roo.factory(config.store, Roo.data);
27180 this.ds = this.store;
27181 this.ds.xmodule = this.xmodule || false;
27184 this.toolbarItems = [];
27185 if (config.items) {
27186 this.toolbarItems = config.items;
27189 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27194 this.bind(this.ds);
27197 if (Roo.bootstrap.version == 4) {
27198 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27200 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27205 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27207 * @cfg {Roo.data.Store} dataSource
27208 * The underlying data store providing the paged data
27211 * @cfg {String/HTMLElement/Element} container
27212 * container The id or element that will contain the toolbar
27215 * @cfg {Boolean} displayInfo
27216 * True to display the displayMsg (defaults to false)
27219 * @cfg {Number} pageSize
27220 * The number of records to display per page (defaults to 20)
27224 * @cfg {String} displayMsg
27225 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27227 displayMsg : 'Displaying {0} - {1} of {2}',
27229 * @cfg {String} emptyMsg
27230 * The message to display when no records are found (defaults to "No data to display")
27232 emptyMsg : 'No data to display',
27234 * Customizable piece of the default paging text (defaults to "Page")
27237 beforePageText : "Page",
27239 * Customizable piece of the default paging text (defaults to "of %0")
27242 afterPageText : "of {0}",
27244 * Customizable piece of the default paging text (defaults to "First Page")
27247 firstText : "First Page",
27249 * Customizable piece of the default paging text (defaults to "Previous Page")
27252 prevText : "Previous Page",
27254 * Customizable piece of the default paging text (defaults to "Next Page")
27257 nextText : "Next Page",
27259 * Customizable piece of the default paging text (defaults to "Last Page")
27262 lastText : "Last Page",
27264 * Customizable piece of the default paging text (defaults to "Refresh")
27267 refreshText : "Refresh",
27271 onRender : function(ct, position)
27273 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27274 this.navgroup.parentId = this.id;
27275 this.navgroup.onRender(this.el, null);
27276 // add the buttons to the navgroup
27278 if(this.displayInfo){
27279 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27280 this.displayEl = this.el.select('.x-paging-info', true).first();
27281 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27282 // this.displayEl = navel.el.select('span',true).first();
27288 Roo.each(_this.buttons, function(e){ // this might need to use render????
27289 Roo.factory(e).render(_this.el);
27293 Roo.each(_this.toolbarItems, function(e) {
27294 _this.navgroup.addItem(e);
27298 this.first = this.navgroup.addItem({
27299 tooltip: this.firstText,
27300 cls: "prev btn-outline-secondary",
27301 html : ' <i class="fa fa-step-backward"></i>',
27303 preventDefault: true,
27304 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27307 this.prev = this.navgroup.addItem({
27308 tooltip: this.prevText,
27309 cls: "prev btn-outline-secondary",
27310 html : ' <i class="fa fa-backward"></i>',
27312 preventDefault: true,
27313 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27315 //this.addSeparator();
27318 var field = this.navgroup.addItem( {
27320 cls : 'x-paging-position btn-outline-secondary',
27322 html : this.beforePageText +
27323 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27324 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27327 this.field = field.el.select('input', true).first();
27328 this.field.on("keydown", this.onPagingKeydown, this);
27329 this.field.on("focus", function(){this.dom.select();});
27332 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27333 //this.field.setHeight(18);
27334 //this.addSeparator();
27335 this.next = this.navgroup.addItem({
27336 tooltip: this.nextText,
27337 cls: "next btn-outline-secondary",
27338 html : ' <i class="fa fa-forward"></i>',
27340 preventDefault: true,
27341 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27343 this.last = this.navgroup.addItem({
27344 tooltip: this.lastText,
27345 html : ' <i class="fa fa-step-forward"></i>',
27346 cls: "next btn-outline-secondary",
27348 preventDefault: true,
27349 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27351 //this.addSeparator();
27352 this.loading = this.navgroup.addItem({
27353 tooltip: this.refreshText,
27354 cls: "btn-outline-secondary",
27355 html : ' <i class="fa fa-refresh"></i>',
27356 preventDefault: true,
27357 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27363 updateInfo : function(){
27364 if(this.displayEl){
27365 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27366 var msg = count == 0 ?
27370 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27372 this.displayEl.update(msg);
27377 onLoad : function(ds, r, o)
27379 this.cursor = o.params && o.params.start ? o.params.start : 0;
27381 var d = this.getPageData(),
27386 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27387 this.field.dom.value = ap;
27388 this.first.setDisabled(ap == 1);
27389 this.prev.setDisabled(ap == 1);
27390 this.next.setDisabled(ap == ps);
27391 this.last.setDisabled(ap == ps);
27392 this.loading.enable();
27397 getPageData : function(){
27398 var total = this.ds.getTotalCount();
27401 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27402 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27407 onLoadError : function(){
27408 this.loading.enable();
27412 onPagingKeydown : function(e){
27413 var k = e.getKey();
27414 var d = this.getPageData();
27416 var v = this.field.dom.value, pageNum;
27417 if(!v || isNaN(pageNum = parseInt(v, 10))){
27418 this.field.dom.value = d.activePage;
27421 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27422 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27425 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))
27427 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27428 this.field.dom.value = pageNum;
27429 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27432 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27434 var v = this.field.dom.value, pageNum;
27435 var increment = (e.shiftKey) ? 10 : 1;
27436 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27439 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27440 this.field.dom.value = d.activePage;
27443 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27445 this.field.dom.value = parseInt(v, 10) + increment;
27446 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27447 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27454 beforeLoad : function(){
27456 this.loading.disable();
27461 onClick : function(which){
27470 ds.load({params:{start: 0, limit: this.pageSize}});
27473 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27476 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27479 var total = ds.getTotalCount();
27480 var extra = total % this.pageSize;
27481 var lastStart = extra ? (total - extra) : total-this.pageSize;
27482 ds.load({params:{start: lastStart, limit: this.pageSize}});
27485 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27491 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27492 * @param {Roo.data.Store} store The data store to unbind
27494 unbind : function(ds){
27495 ds.un("beforeload", this.beforeLoad, this);
27496 ds.un("load", this.onLoad, this);
27497 ds.un("loadexception", this.onLoadError, this);
27498 ds.un("remove", this.updateInfo, this);
27499 ds.un("add", this.updateInfo, this);
27500 this.ds = undefined;
27504 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27505 * @param {Roo.data.Store} store The data store to bind
27507 bind : function(ds){
27508 ds.on("beforeload", this.beforeLoad, this);
27509 ds.on("load", this.onLoad, this);
27510 ds.on("loadexception", this.onLoadError, this);
27511 ds.on("remove", this.updateInfo, this);
27512 ds.on("add", this.updateInfo, this);
27523 * @class Roo.bootstrap.MessageBar
27524 * @extends Roo.bootstrap.Component
27525 * Bootstrap MessageBar class
27526 * @cfg {String} html contents of the MessageBar
27527 * @cfg {String} weight (info | success | warning | danger) default info
27528 * @cfg {String} beforeClass insert the bar before the given class
27529 * @cfg {Boolean} closable (true | false) default false
27530 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27533 * Create a new Element
27534 * @param {Object} config The config object
27537 Roo.bootstrap.MessageBar = function(config){
27538 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27541 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27547 beforeClass: 'bootstrap-sticky-wrap',
27549 getAutoCreate : function(){
27553 cls: 'alert alert-dismissable alert-' + this.weight,
27558 html: this.html || ''
27564 cfg.cls += ' alert-messages-fixed';
27578 onRender : function(ct, position)
27580 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27583 var cfg = Roo.apply({}, this.getAutoCreate());
27587 cfg.cls += ' ' + this.cls;
27590 cfg.style = this.style;
27592 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27594 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27597 this.el.select('>button.close').on('click', this.hide, this);
27603 if (!this.rendered) {
27609 this.fireEvent('show', this);
27615 if (!this.rendered) {
27621 this.fireEvent('hide', this);
27624 update : function()
27626 // var e = this.el.dom.firstChild;
27628 // if(this.closable){
27629 // e = e.nextSibling;
27632 // e.data = this.html || '';
27634 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27650 * @class Roo.bootstrap.Graph
27651 * @extends Roo.bootstrap.Component
27652 * Bootstrap Graph class
27656 @cfg {String} graphtype bar | vbar | pie
27657 @cfg {number} g_x coodinator | centre x (pie)
27658 @cfg {number} g_y coodinator | centre y (pie)
27659 @cfg {number} g_r radius (pie)
27660 @cfg {number} g_height height of the chart (respected by all elements in the set)
27661 @cfg {number} g_width width of the chart (respected by all elements in the set)
27662 @cfg {Object} title The title of the chart
27665 -opts (object) options for the chart
27667 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27668 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27670 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.
27671 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27673 o stretch (boolean)
27675 -opts (object) options for the pie
27678 o startAngle (number)
27679 o endAngle (number)
27683 * Create a new Input
27684 * @param {Object} config The config object
27687 Roo.bootstrap.Graph = function(config){
27688 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27694 * The img click event for the img.
27695 * @param {Roo.EventObject} e
27701 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27712 //g_colors: this.colors,
27719 getAutoCreate : function(){
27730 onRender : function(ct,position){
27733 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27735 if (typeof(Raphael) == 'undefined') {
27736 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27740 this.raphael = Raphael(this.el.dom);
27742 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27743 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27744 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27745 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27747 r.text(160, 10, "Single Series Chart").attr(txtattr);
27748 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27749 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27750 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27752 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27753 r.barchart(330, 10, 300, 220, data1);
27754 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27755 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27758 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27759 // r.barchart(30, 30, 560, 250, xdata, {
27760 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27761 // axis : "0 0 1 1",
27762 // axisxlabels : xdata
27763 // //yvalues : cols,
27766 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27768 // this.load(null,xdata,{
27769 // axis : "0 0 1 1",
27770 // axisxlabels : xdata
27775 load : function(graphtype,xdata,opts)
27777 this.raphael.clear();
27779 graphtype = this.graphtype;
27784 var r = this.raphael,
27785 fin = function () {
27786 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27788 fout = function () {
27789 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27791 pfin = function() {
27792 this.sector.stop();
27793 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27796 this.label[0].stop();
27797 this.label[0].attr({ r: 7.5 });
27798 this.label[1].attr({ "font-weight": 800 });
27801 pfout = function() {
27802 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27805 this.label[0].animate({ r: 5 }, 500, "bounce");
27806 this.label[1].attr({ "font-weight": 400 });
27812 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27815 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27818 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27819 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27821 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27828 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27833 setTitle: function(o)
27838 initEvents: function() {
27841 this.el.on('click', this.onClick, this);
27845 onClick : function(e)
27847 Roo.log('img onclick');
27848 this.fireEvent('click', this, e);
27860 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27863 * @class Roo.bootstrap.dash.NumberBox
27864 * @extends Roo.bootstrap.Component
27865 * Bootstrap NumberBox class
27866 * @cfg {String} headline Box headline
27867 * @cfg {String} content Box content
27868 * @cfg {String} icon Box icon
27869 * @cfg {String} footer Footer text
27870 * @cfg {String} fhref Footer href
27873 * Create a new NumberBox
27874 * @param {Object} config The config object
27878 Roo.bootstrap.dash.NumberBox = function(config){
27879 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27883 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27892 getAutoCreate : function(){
27896 cls : 'small-box ',
27904 cls : 'roo-headline',
27905 html : this.headline
27909 cls : 'roo-content',
27910 html : this.content
27924 cls : 'ion ' + this.icon
27933 cls : 'small-box-footer',
27934 href : this.fhref || '#',
27938 cfg.cn.push(footer);
27945 onRender : function(ct,position){
27946 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27953 setHeadline: function (value)
27955 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27958 setFooter: function (value, href)
27960 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27963 this.el.select('a.small-box-footer',true).first().attr('href', href);
27968 setContent: function (value)
27970 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27973 initEvents: function()
27987 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27990 * @class Roo.bootstrap.dash.TabBox
27991 * @extends Roo.bootstrap.Component
27992 * Bootstrap TabBox class
27993 * @cfg {String} title Title of the TabBox
27994 * @cfg {String} icon Icon of the TabBox
27995 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27996 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27999 * Create a new TabBox
28000 * @param {Object} config The config object
28004 Roo.bootstrap.dash.TabBox = function(config){
28005 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28010 * When a pane is added
28011 * @param {Roo.bootstrap.dash.TabPane} pane
28015 * @event activatepane
28016 * When a pane is activated
28017 * @param {Roo.bootstrap.dash.TabPane} pane
28019 "activatepane" : true
28027 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28032 tabScrollable : false,
28034 getChildContainer : function()
28036 return this.el.select('.tab-content', true).first();
28039 getAutoCreate : function(){
28043 cls: 'pull-left header',
28051 cls: 'fa ' + this.icon
28057 cls: 'nav nav-tabs pull-right',
28063 if(this.tabScrollable){
28070 cls: 'nav nav-tabs pull-right',
28081 cls: 'nav-tabs-custom',
28086 cls: 'tab-content no-padding',
28094 initEvents : function()
28096 //Roo.log('add add pane handler');
28097 this.on('addpane', this.onAddPane, this);
28100 * Updates the box title
28101 * @param {String} html to set the title to.
28103 setTitle : function(value)
28105 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28107 onAddPane : function(pane)
28109 this.panes.push(pane);
28110 //Roo.log('addpane');
28112 // tabs are rendere left to right..
28113 if(!this.showtabs){
28117 var ctr = this.el.select('.nav-tabs', true).first();
28120 var existing = ctr.select('.nav-tab',true);
28121 var qty = existing.getCount();;
28124 var tab = ctr.createChild({
28126 cls : 'nav-tab' + (qty ? '' : ' active'),
28134 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28137 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28139 pane.el.addClass('active');
28144 onTabClick : function(ev,un,ob,pane)
28146 //Roo.log('tab - prev default');
28147 ev.preventDefault();
28150 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28151 pane.tab.addClass('active');
28152 //Roo.log(pane.title);
28153 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28154 // technically we should have a deactivate event.. but maybe add later.
28155 // and it should not de-activate the selected tab...
28156 this.fireEvent('activatepane', pane);
28157 pane.el.addClass('active');
28158 pane.fireEvent('activate');
28163 getActivePane : function()
28166 Roo.each(this.panes, function(p) {
28167 if(p.el.hasClass('active')){
28188 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28190 * @class Roo.bootstrap.TabPane
28191 * @extends Roo.bootstrap.Component
28192 * Bootstrap TabPane class
28193 * @cfg {Boolean} active (false | true) Default false
28194 * @cfg {String} title title of panel
28198 * Create a new TabPane
28199 * @param {Object} config The config object
28202 Roo.bootstrap.dash.TabPane = function(config){
28203 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28209 * When a pane is activated
28210 * @param {Roo.bootstrap.dash.TabPane} pane
28217 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28222 // the tabBox that this is attached to.
28225 getAutoCreate : function()
28233 cfg.cls += ' active';
28238 initEvents : function()
28240 //Roo.log('trigger add pane handler');
28241 this.parent().fireEvent('addpane', this)
28245 * Updates the tab title
28246 * @param {String} html to set the title to.
28248 setTitle: function(str)
28254 this.tab.select('a', true).first().dom.innerHTML = str;
28271 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28274 * @class Roo.bootstrap.menu.Menu
28275 * @extends Roo.bootstrap.Component
28276 * Bootstrap Menu class - container for Menu
28277 * @cfg {String} html Text of the menu
28278 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28279 * @cfg {String} icon Font awesome icon
28280 * @cfg {String} pos Menu align to (top | bottom) default bottom
28284 * Create a new Menu
28285 * @param {Object} config The config object
28289 Roo.bootstrap.menu.Menu = function(config){
28290 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28294 * @event beforeshow
28295 * Fires before this menu is displayed
28296 * @param {Roo.bootstrap.menu.Menu} this
28300 * @event beforehide
28301 * Fires before this menu is hidden
28302 * @param {Roo.bootstrap.menu.Menu} this
28307 * Fires after this menu is displayed
28308 * @param {Roo.bootstrap.menu.Menu} this
28313 * Fires after this menu is hidden
28314 * @param {Roo.bootstrap.menu.Menu} this
28319 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28320 * @param {Roo.bootstrap.menu.Menu} this
28321 * @param {Roo.EventObject} e
28328 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28332 weight : 'default',
28337 getChildContainer : function() {
28338 if(this.isSubMenu){
28342 return this.el.select('ul.dropdown-menu', true).first();
28345 getAutoCreate : function()
28350 cls : 'roo-menu-text',
28358 cls : 'fa ' + this.icon
28369 cls : 'dropdown-button btn btn-' + this.weight,
28374 cls : 'dropdown-toggle btn btn-' + this.weight,
28384 cls : 'dropdown-menu'
28390 if(this.pos == 'top'){
28391 cfg.cls += ' dropup';
28394 if(this.isSubMenu){
28397 cls : 'dropdown-menu'
28404 onRender : function(ct, position)
28406 this.isSubMenu = ct.hasClass('dropdown-submenu');
28408 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28411 initEvents : function()
28413 if(this.isSubMenu){
28417 this.hidden = true;
28419 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28420 this.triggerEl.on('click', this.onTriggerPress, this);
28422 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28423 this.buttonEl.on('click', this.onClick, this);
28429 if(this.isSubMenu){
28433 return this.el.select('ul.dropdown-menu', true).first();
28436 onClick : function(e)
28438 this.fireEvent("click", this, e);
28441 onTriggerPress : function(e)
28443 if (this.isVisible()) {
28450 isVisible : function(){
28451 return !this.hidden;
28456 this.fireEvent("beforeshow", this);
28458 this.hidden = false;
28459 this.el.addClass('open');
28461 Roo.get(document).on("mouseup", this.onMouseUp, this);
28463 this.fireEvent("show", this);
28470 this.fireEvent("beforehide", this);
28472 this.hidden = true;
28473 this.el.removeClass('open');
28475 Roo.get(document).un("mouseup", this.onMouseUp);
28477 this.fireEvent("hide", this);
28480 onMouseUp : function()
28494 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28497 * @class Roo.bootstrap.menu.Item
28498 * @extends Roo.bootstrap.Component
28499 * Bootstrap MenuItem class
28500 * @cfg {Boolean} submenu (true | false) default false
28501 * @cfg {String} html text of the item
28502 * @cfg {String} href the link
28503 * @cfg {Boolean} disable (true | false) default false
28504 * @cfg {Boolean} preventDefault (true | false) default true
28505 * @cfg {String} icon Font awesome icon
28506 * @cfg {String} pos Submenu align to (left | right) default right
28510 * Create a new Item
28511 * @param {Object} config The config object
28515 Roo.bootstrap.menu.Item = function(config){
28516 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28520 * Fires when the mouse is hovering over this menu
28521 * @param {Roo.bootstrap.menu.Item} this
28522 * @param {Roo.EventObject} e
28527 * Fires when the mouse exits this menu
28528 * @param {Roo.bootstrap.menu.Item} this
28529 * @param {Roo.EventObject} e
28535 * The raw click event for the entire grid.
28536 * @param {Roo.EventObject} e
28542 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28547 preventDefault: true,
28552 getAutoCreate : function()
28557 cls : 'roo-menu-item-text',
28565 cls : 'fa ' + this.icon
28574 href : this.href || '#',
28581 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28585 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28587 if(this.pos == 'left'){
28588 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28595 initEvents : function()
28597 this.el.on('mouseover', this.onMouseOver, this);
28598 this.el.on('mouseout', this.onMouseOut, this);
28600 this.el.select('a', true).first().on('click', this.onClick, this);
28604 onClick : function(e)
28606 if(this.preventDefault){
28607 e.preventDefault();
28610 this.fireEvent("click", this, e);
28613 onMouseOver : function(e)
28615 if(this.submenu && this.pos == 'left'){
28616 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28619 this.fireEvent("mouseover", this, e);
28622 onMouseOut : function(e)
28624 this.fireEvent("mouseout", this, e);
28636 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28639 * @class Roo.bootstrap.menu.Separator
28640 * @extends Roo.bootstrap.Component
28641 * Bootstrap Separator class
28644 * Create a new Separator
28645 * @param {Object} config The config object
28649 Roo.bootstrap.menu.Separator = function(config){
28650 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28653 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28655 getAutoCreate : function(){
28676 * @class Roo.bootstrap.Tooltip
28677 * Bootstrap Tooltip class
28678 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28679 * to determine which dom element triggers the tooltip.
28681 * It needs to add support for additional attributes like tooltip-position
28684 * Create a new Toolti
28685 * @param {Object} config The config object
28688 Roo.bootstrap.Tooltip = function(config){
28689 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28691 this.alignment = Roo.bootstrap.Tooltip.alignment;
28693 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28694 this.alignment = config.alignment;
28699 Roo.apply(Roo.bootstrap.Tooltip, {
28701 * @function init initialize tooltip monitoring.
28705 currentTip : false,
28706 currentRegion : false,
28712 Roo.get(document).on('mouseover', this.enter ,this);
28713 Roo.get(document).on('mouseout', this.leave, this);
28716 this.currentTip = new Roo.bootstrap.Tooltip();
28719 enter : function(ev)
28721 var dom = ev.getTarget();
28723 //Roo.log(['enter',dom]);
28724 var el = Roo.fly(dom);
28725 if (this.currentEl) {
28727 //Roo.log(this.currentEl);
28728 //Roo.log(this.currentEl.contains(dom));
28729 if (this.currentEl == el) {
28732 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28738 if (this.currentTip.el) {
28739 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28743 if(!el || el.dom == document){
28749 // you can not look for children, as if el is the body.. then everythign is the child..
28750 if (!el.attr('tooltip')) { //
28751 if (!el.select("[tooltip]").elements.length) {
28754 // is the mouse over this child...?
28755 bindEl = el.select("[tooltip]").first();
28756 var xy = ev.getXY();
28757 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28758 //Roo.log("not in region.");
28761 //Roo.log("child element over..");
28764 this.currentEl = bindEl;
28765 this.currentTip.bind(bindEl);
28766 this.currentRegion = Roo.lib.Region.getRegion(dom);
28767 this.currentTip.enter();
28770 leave : function(ev)
28772 var dom = ev.getTarget();
28773 //Roo.log(['leave',dom]);
28774 if (!this.currentEl) {
28779 if (dom != this.currentEl.dom) {
28782 var xy = ev.getXY();
28783 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28786 // only activate leave if mouse cursor is outside... bounding box..
28791 if (this.currentTip) {
28792 this.currentTip.leave();
28794 //Roo.log('clear currentEl');
28795 this.currentEl = false;
28800 'left' : ['r-l', [-2,0], 'right'],
28801 'right' : ['l-r', [2,0], 'left'],
28802 'bottom' : ['t-b', [0,2], 'top'],
28803 'top' : [ 'b-t', [0,-2], 'bottom']
28809 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28814 delay : null, // can be { show : 300 , hide: 500}
28818 hoverState : null, //???
28820 placement : 'bottom',
28824 getAutoCreate : function(){
28831 cls : 'tooltip-arrow arrow'
28834 cls : 'tooltip-inner'
28841 bind : function(el)
28846 initEvents : function()
28848 this.arrowEl = this.el.select('.arrow', true).first();
28849 this.innerEl = this.el.select('.tooltip-inner', true).first();
28852 enter : function () {
28854 if (this.timeout != null) {
28855 clearTimeout(this.timeout);
28858 this.hoverState = 'in';
28859 //Roo.log("enter - show");
28860 if (!this.delay || !this.delay.show) {
28865 this.timeout = setTimeout(function () {
28866 if (_t.hoverState == 'in') {
28869 }, this.delay.show);
28873 clearTimeout(this.timeout);
28875 this.hoverState = 'out';
28876 if (!this.delay || !this.delay.hide) {
28882 this.timeout = setTimeout(function () {
28883 //Roo.log("leave - timeout");
28885 if (_t.hoverState == 'out') {
28887 Roo.bootstrap.Tooltip.currentEl = false;
28892 show : function (msg)
28895 this.render(document.body);
28898 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28900 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28902 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28904 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28905 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28907 var placement = typeof this.placement == 'function' ?
28908 this.placement.call(this, this.el, on_el) :
28911 var autoToken = /\s?auto?\s?/i;
28912 var autoPlace = autoToken.test(placement);
28914 placement = placement.replace(autoToken, '') || 'top';
28918 //this.el.setXY([0,0]);
28920 //this.el.dom.style.display='block';
28922 //this.el.appendTo(on_el);
28924 var p = this.getPosition();
28925 var box = this.el.getBox();
28931 var align = this.alignment[placement];
28933 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28935 if(placement == 'top' || placement == 'bottom'){
28937 placement = 'right';
28940 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28941 placement = 'left';
28944 var scroll = Roo.select('body', true).first().getScroll();
28946 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28950 align = this.alignment[placement];
28952 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28956 this.el.alignTo(this.bindEl, align[0],align[1]);
28957 //var arrow = this.el.select('.arrow',true).first();
28958 //arrow.set(align[2],
28960 this.el.addClass(placement);
28961 this.el.addClass("bs-tooltip-"+ placement);
28963 this.el.addClass('in fade show');
28965 this.hoverState = null;
28967 if (this.el.hasClass('fade')) {
28982 //this.el.setXY([0,0]);
28983 this.el.removeClass(['show', 'in']);
28999 * @class Roo.bootstrap.LocationPicker
29000 * @extends Roo.bootstrap.Component
29001 * Bootstrap LocationPicker class
29002 * @cfg {Number} latitude Position when init default 0
29003 * @cfg {Number} longitude Position when init default 0
29004 * @cfg {Number} zoom default 15
29005 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29006 * @cfg {Boolean} mapTypeControl default false
29007 * @cfg {Boolean} disableDoubleClickZoom default false
29008 * @cfg {Boolean} scrollwheel default true
29009 * @cfg {Boolean} streetViewControl default false
29010 * @cfg {Number} radius default 0
29011 * @cfg {String} locationName
29012 * @cfg {Boolean} draggable default true
29013 * @cfg {Boolean} enableAutocomplete default false
29014 * @cfg {Boolean} enableReverseGeocode default true
29015 * @cfg {String} markerTitle
29018 * Create a new LocationPicker
29019 * @param {Object} config The config object
29023 Roo.bootstrap.LocationPicker = function(config){
29025 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29030 * Fires when the picker initialized.
29031 * @param {Roo.bootstrap.LocationPicker} this
29032 * @param {Google Location} location
29036 * @event positionchanged
29037 * Fires when the picker position changed.
29038 * @param {Roo.bootstrap.LocationPicker} this
29039 * @param {Google Location} location
29041 positionchanged : true,
29044 * Fires when the map resize.
29045 * @param {Roo.bootstrap.LocationPicker} this
29050 * Fires when the map show.
29051 * @param {Roo.bootstrap.LocationPicker} this
29056 * Fires when the map hide.
29057 * @param {Roo.bootstrap.LocationPicker} this
29062 * Fires when click the map.
29063 * @param {Roo.bootstrap.LocationPicker} this
29064 * @param {Map event} e
29068 * @event mapRightClick
29069 * Fires when right click the map.
29070 * @param {Roo.bootstrap.LocationPicker} this
29071 * @param {Map event} e
29073 mapRightClick : true,
29075 * @event markerClick
29076 * Fires when click the marker.
29077 * @param {Roo.bootstrap.LocationPicker} this
29078 * @param {Map event} e
29080 markerClick : true,
29082 * @event markerRightClick
29083 * Fires when right click the marker.
29084 * @param {Roo.bootstrap.LocationPicker} this
29085 * @param {Map event} e
29087 markerRightClick : true,
29089 * @event OverlayViewDraw
29090 * Fires when OverlayView Draw
29091 * @param {Roo.bootstrap.LocationPicker} this
29093 OverlayViewDraw : true,
29095 * @event OverlayViewOnAdd
29096 * Fires when OverlayView Draw
29097 * @param {Roo.bootstrap.LocationPicker} this
29099 OverlayViewOnAdd : true,
29101 * @event OverlayViewOnRemove
29102 * Fires when OverlayView Draw
29103 * @param {Roo.bootstrap.LocationPicker} this
29105 OverlayViewOnRemove : true,
29107 * @event OverlayViewShow
29108 * Fires when OverlayView Draw
29109 * @param {Roo.bootstrap.LocationPicker} this
29110 * @param {Pixel} cpx
29112 OverlayViewShow : true,
29114 * @event OverlayViewHide
29115 * Fires when OverlayView Draw
29116 * @param {Roo.bootstrap.LocationPicker} this
29118 OverlayViewHide : true,
29120 * @event loadexception
29121 * Fires when load google lib failed.
29122 * @param {Roo.bootstrap.LocationPicker} this
29124 loadexception : true
29129 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29131 gMapContext: false,
29137 mapTypeControl: false,
29138 disableDoubleClickZoom: false,
29140 streetViewControl: false,
29144 enableAutocomplete: false,
29145 enableReverseGeocode: true,
29148 getAutoCreate: function()
29153 cls: 'roo-location-picker'
29159 initEvents: function(ct, position)
29161 if(!this.el.getWidth() || this.isApplied()){
29165 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29170 initial: function()
29172 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29173 this.fireEvent('loadexception', this);
29177 if(!this.mapTypeId){
29178 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29181 this.gMapContext = this.GMapContext();
29183 this.initOverlayView();
29185 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29189 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29190 _this.setPosition(_this.gMapContext.marker.position);
29193 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29194 _this.fireEvent('mapClick', this, event);
29198 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29199 _this.fireEvent('mapRightClick', this, event);
29203 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29204 _this.fireEvent('markerClick', this, event);
29208 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29209 _this.fireEvent('markerRightClick', this, event);
29213 this.setPosition(this.gMapContext.location);
29215 this.fireEvent('initial', this, this.gMapContext.location);
29218 initOverlayView: function()
29222 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29226 _this.fireEvent('OverlayViewDraw', _this);
29231 _this.fireEvent('OverlayViewOnAdd', _this);
29234 onRemove: function()
29236 _this.fireEvent('OverlayViewOnRemove', _this);
29239 show: function(cpx)
29241 _this.fireEvent('OverlayViewShow', _this, cpx);
29246 _this.fireEvent('OverlayViewHide', _this);
29252 fromLatLngToContainerPixel: function(event)
29254 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29257 isApplied: function()
29259 return this.getGmapContext() == false ? false : true;
29262 getGmapContext: function()
29264 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29267 GMapContext: function()
29269 var position = new google.maps.LatLng(this.latitude, this.longitude);
29271 var _map = new google.maps.Map(this.el.dom, {
29274 mapTypeId: this.mapTypeId,
29275 mapTypeControl: this.mapTypeControl,
29276 disableDoubleClickZoom: this.disableDoubleClickZoom,
29277 scrollwheel: this.scrollwheel,
29278 streetViewControl: this.streetViewControl,
29279 locationName: this.locationName,
29280 draggable: this.draggable,
29281 enableAutocomplete: this.enableAutocomplete,
29282 enableReverseGeocode: this.enableReverseGeocode
29285 var _marker = new google.maps.Marker({
29286 position: position,
29288 title: this.markerTitle,
29289 draggable: this.draggable
29296 location: position,
29297 radius: this.radius,
29298 locationName: this.locationName,
29299 addressComponents: {
29300 formatted_address: null,
29301 addressLine1: null,
29302 addressLine2: null,
29304 streetNumber: null,
29308 stateOrProvince: null
29311 domContainer: this.el.dom,
29312 geodecoder: new google.maps.Geocoder()
29316 drawCircle: function(center, radius, options)
29318 if (this.gMapContext.circle != null) {
29319 this.gMapContext.circle.setMap(null);
29323 options = Roo.apply({}, options, {
29324 strokeColor: "#0000FF",
29325 strokeOpacity: .35,
29327 fillColor: "#0000FF",
29331 options.map = this.gMapContext.map;
29332 options.radius = radius;
29333 options.center = center;
29334 this.gMapContext.circle = new google.maps.Circle(options);
29335 return this.gMapContext.circle;
29341 setPosition: function(location)
29343 this.gMapContext.location = location;
29344 this.gMapContext.marker.setPosition(location);
29345 this.gMapContext.map.panTo(location);
29346 this.drawCircle(location, this.gMapContext.radius, {});
29350 if (this.gMapContext.settings.enableReverseGeocode) {
29351 this.gMapContext.geodecoder.geocode({
29352 latLng: this.gMapContext.location
29353 }, function(results, status) {
29355 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29356 _this.gMapContext.locationName = results[0].formatted_address;
29357 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29359 _this.fireEvent('positionchanged', this, location);
29366 this.fireEvent('positionchanged', this, location);
29371 google.maps.event.trigger(this.gMapContext.map, "resize");
29373 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29375 this.fireEvent('resize', this);
29378 setPositionByLatLng: function(latitude, longitude)
29380 this.setPosition(new google.maps.LatLng(latitude, longitude));
29383 getCurrentPosition: function()
29386 latitude: this.gMapContext.location.lat(),
29387 longitude: this.gMapContext.location.lng()
29391 getAddressName: function()
29393 return this.gMapContext.locationName;
29396 getAddressComponents: function()
29398 return this.gMapContext.addressComponents;
29401 address_component_from_google_geocode: function(address_components)
29405 for (var i = 0; i < address_components.length; i++) {
29406 var component = address_components[i];
29407 if (component.types.indexOf("postal_code") >= 0) {
29408 result.postalCode = component.short_name;
29409 } else if (component.types.indexOf("street_number") >= 0) {
29410 result.streetNumber = component.short_name;
29411 } else if (component.types.indexOf("route") >= 0) {
29412 result.streetName = component.short_name;
29413 } else if (component.types.indexOf("neighborhood") >= 0) {
29414 result.city = component.short_name;
29415 } else if (component.types.indexOf("locality") >= 0) {
29416 result.city = component.short_name;
29417 } else if (component.types.indexOf("sublocality") >= 0) {
29418 result.district = component.short_name;
29419 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29420 result.stateOrProvince = component.short_name;
29421 } else if (component.types.indexOf("country") >= 0) {
29422 result.country = component.short_name;
29426 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29427 result.addressLine2 = "";
29431 setZoomLevel: function(zoom)
29433 this.gMapContext.map.setZoom(zoom);
29446 this.fireEvent('show', this);
29457 this.fireEvent('hide', this);
29462 Roo.apply(Roo.bootstrap.LocationPicker, {
29464 OverlayView : function(map, options)
29466 options = options || {};
29473 * @class Roo.bootstrap.Alert
29474 * @extends Roo.bootstrap.Component
29475 * Bootstrap Alert class - shows an alert area box
29477 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29478 Enter a valid email address
29481 * @cfg {String} title The title of alert
29482 * @cfg {String} html The content of alert
29483 * @cfg {String} weight ( success | info | warning | danger )
29484 * @cfg {String} faicon font-awesomeicon
29487 * Create a new alert
29488 * @param {Object} config The config object
29492 Roo.bootstrap.Alert = function(config){
29493 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29497 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29504 getAutoCreate : function()
29513 cls : 'roo-alert-icon'
29518 cls : 'roo-alert-title',
29523 cls : 'roo-alert-text',
29530 cfg.cn[0].cls += ' fa ' + this.faicon;
29534 cfg.cls += ' alert-' + this.weight;
29540 initEvents: function()
29542 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29545 setTitle : function(str)
29547 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29550 setText : function(str)
29552 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29555 setWeight : function(weight)
29558 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29561 this.weight = weight;
29563 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29566 setIcon : function(icon)
29569 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29572 this.faicon = icon;
29574 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29595 * @class Roo.bootstrap.UploadCropbox
29596 * @extends Roo.bootstrap.Component
29597 * Bootstrap UploadCropbox class
29598 * @cfg {String} emptyText show when image has been loaded
29599 * @cfg {String} rotateNotify show when image too small to rotate
29600 * @cfg {Number} errorTimeout default 3000
29601 * @cfg {Number} minWidth default 300
29602 * @cfg {Number} minHeight default 300
29603 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29604 * @cfg {Boolean} isDocument (true|false) default false
29605 * @cfg {String} url action url
29606 * @cfg {String} paramName default 'imageUpload'
29607 * @cfg {String} method default POST
29608 * @cfg {Boolean} loadMask (true|false) default true
29609 * @cfg {Boolean} loadingText default 'Loading...'
29612 * Create a new UploadCropbox
29613 * @param {Object} config The config object
29616 Roo.bootstrap.UploadCropbox = function(config){
29617 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29621 * @event beforeselectfile
29622 * Fire before select file
29623 * @param {Roo.bootstrap.UploadCropbox} this
29625 "beforeselectfile" : true,
29628 * Fire after initEvent
29629 * @param {Roo.bootstrap.UploadCropbox} this
29634 * Fire after initEvent
29635 * @param {Roo.bootstrap.UploadCropbox} this
29636 * @param {String} data
29641 * Fire when preparing the file data
29642 * @param {Roo.bootstrap.UploadCropbox} this
29643 * @param {Object} file
29648 * Fire when get exception
29649 * @param {Roo.bootstrap.UploadCropbox} this
29650 * @param {XMLHttpRequest} xhr
29652 "exception" : true,
29654 * @event beforeloadcanvas
29655 * Fire before load the canvas
29656 * @param {Roo.bootstrap.UploadCropbox} this
29657 * @param {String} src
29659 "beforeloadcanvas" : true,
29662 * Fire when trash image
29663 * @param {Roo.bootstrap.UploadCropbox} this
29668 * Fire when download the image
29669 * @param {Roo.bootstrap.UploadCropbox} this
29673 * @event footerbuttonclick
29674 * Fire when footerbuttonclick
29675 * @param {Roo.bootstrap.UploadCropbox} this
29676 * @param {String} type
29678 "footerbuttonclick" : true,
29682 * @param {Roo.bootstrap.UploadCropbox} this
29687 * Fire when rotate the image
29688 * @param {Roo.bootstrap.UploadCropbox} this
29689 * @param {String} pos
29694 * Fire when inspect the file
29695 * @param {Roo.bootstrap.UploadCropbox} this
29696 * @param {Object} file
29701 * Fire when xhr upload the file
29702 * @param {Roo.bootstrap.UploadCropbox} this
29703 * @param {Object} data
29708 * Fire when arrange the file data
29709 * @param {Roo.bootstrap.UploadCropbox} this
29710 * @param {Object} formData
29715 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29718 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29720 emptyText : 'Click to upload image',
29721 rotateNotify : 'Image is too small to rotate',
29722 errorTimeout : 3000,
29736 cropType : 'image/jpeg',
29738 canvasLoaded : false,
29739 isDocument : false,
29741 paramName : 'imageUpload',
29743 loadingText : 'Loading...',
29746 getAutoCreate : function()
29750 cls : 'roo-upload-cropbox',
29754 cls : 'roo-upload-cropbox-selector',
29759 cls : 'roo-upload-cropbox-body',
29760 style : 'cursor:pointer',
29764 cls : 'roo-upload-cropbox-preview'
29768 cls : 'roo-upload-cropbox-thumb'
29772 cls : 'roo-upload-cropbox-empty-notify',
29773 html : this.emptyText
29777 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29778 html : this.rotateNotify
29784 cls : 'roo-upload-cropbox-footer',
29787 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29797 onRender : function(ct, position)
29799 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29801 if (this.buttons.length) {
29803 Roo.each(this.buttons, function(bb) {
29805 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29807 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29813 this.maskEl = this.el;
29817 initEvents : function()
29819 this.urlAPI = (window.createObjectURL && window) ||
29820 (window.URL && URL.revokeObjectURL && URL) ||
29821 (window.webkitURL && webkitURL);
29823 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29824 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29826 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29827 this.selectorEl.hide();
29829 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29830 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29832 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29833 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29834 this.thumbEl.hide();
29836 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29837 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29839 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29840 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29841 this.errorEl.hide();
29843 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29844 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29845 this.footerEl.hide();
29847 this.setThumbBoxSize();
29853 this.fireEvent('initial', this);
29860 window.addEventListener("resize", function() { _this.resize(); } );
29862 this.bodyEl.on('click', this.beforeSelectFile, this);
29865 this.bodyEl.on('touchstart', this.onTouchStart, this);
29866 this.bodyEl.on('touchmove', this.onTouchMove, this);
29867 this.bodyEl.on('touchend', this.onTouchEnd, this);
29871 this.bodyEl.on('mousedown', this.onMouseDown, this);
29872 this.bodyEl.on('mousemove', this.onMouseMove, this);
29873 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29874 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29875 Roo.get(document).on('mouseup', this.onMouseUp, this);
29878 this.selectorEl.on('change', this.onFileSelected, this);
29884 this.baseScale = 1;
29886 this.baseRotate = 1;
29887 this.dragable = false;
29888 this.pinching = false;
29891 this.cropData = false;
29892 this.notifyEl.dom.innerHTML = this.emptyText;
29894 this.selectorEl.dom.value = '';
29898 resize : function()
29900 if(this.fireEvent('resize', this) != false){
29901 this.setThumbBoxPosition();
29902 this.setCanvasPosition();
29906 onFooterButtonClick : function(e, el, o, type)
29909 case 'rotate-left' :
29910 this.onRotateLeft(e);
29912 case 'rotate-right' :
29913 this.onRotateRight(e);
29916 this.beforeSelectFile(e);
29931 this.fireEvent('footerbuttonclick', this, type);
29934 beforeSelectFile : function(e)
29936 e.preventDefault();
29938 if(this.fireEvent('beforeselectfile', this) != false){
29939 this.selectorEl.dom.click();
29943 onFileSelected : function(e)
29945 e.preventDefault();
29947 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29951 var file = this.selectorEl.dom.files[0];
29953 if(this.fireEvent('inspect', this, file) != false){
29954 this.prepare(file);
29959 trash : function(e)
29961 this.fireEvent('trash', this);
29964 download : function(e)
29966 this.fireEvent('download', this);
29969 loadCanvas : function(src)
29971 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29975 this.imageEl = document.createElement('img');
29979 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29981 this.imageEl.src = src;
29985 onLoadCanvas : function()
29987 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29988 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29990 this.bodyEl.un('click', this.beforeSelectFile, this);
29992 this.notifyEl.hide();
29993 this.thumbEl.show();
29994 this.footerEl.show();
29996 this.baseRotateLevel();
29998 if(this.isDocument){
29999 this.setThumbBoxSize();
30002 this.setThumbBoxPosition();
30004 this.baseScaleLevel();
30010 this.canvasLoaded = true;
30013 this.maskEl.unmask();
30018 setCanvasPosition : function()
30020 if(!this.canvasEl){
30024 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30025 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30027 this.previewEl.setLeft(pw);
30028 this.previewEl.setTop(ph);
30032 onMouseDown : function(e)
30036 this.dragable = true;
30037 this.pinching = false;
30039 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30040 this.dragable = false;
30044 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30045 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30049 onMouseMove : function(e)
30053 if(!this.canvasLoaded){
30057 if (!this.dragable){
30061 var minX = Math.ceil(this.thumbEl.getLeft(true));
30062 var minY = Math.ceil(this.thumbEl.getTop(true));
30064 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30065 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30067 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30068 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30070 x = x - this.mouseX;
30071 y = y - this.mouseY;
30073 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30074 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30076 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30077 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30079 this.previewEl.setLeft(bgX);
30080 this.previewEl.setTop(bgY);
30082 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30083 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30086 onMouseUp : function(e)
30090 this.dragable = false;
30093 onMouseWheel : function(e)
30097 this.startScale = this.scale;
30099 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30101 if(!this.zoomable()){
30102 this.scale = this.startScale;
30111 zoomable : function()
30113 var minScale = this.thumbEl.getWidth() / this.minWidth;
30115 if(this.minWidth < this.minHeight){
30116 minScale = this.thumbEl.getHeight() / this.minHeight;
30119 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30120 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30124 (this.rotate == 0 || this.rotate == 180) &&
30126 width > this.imageEl.OriginWidth ||
30127 height > this.imageEl.OriginHeight ||
30128 (width < this.minWidth && height < this.minHeight)
30136 (this.rotate == 90 || this.rotate == 270) &&
30138 width > this.imageEl.OriginWidth ||
30139 height > this.imageEl.OriginHeight ||
30140 (width < this.minHeight && height < this.minWidth)
30147 !this.isDocument &&
30148 (this.rotate == 0 || this.rotate == 180) &&
30150 width < this.minWidth ||
30151 width > this.imageEl.OriginWidth ||
30152 height < this.minHeight ||
30153 height > this.imageEl.OriginHeight
30160 !this.isDocument &&
30161 (this.rotate == 90 || this.rotate == 270) &&
30163 width < this.minHeight ||
30164 width > this.imageEl.OriginWidth ||
30165 height < this.minWidth ||
30166 height > this.imageEl.OriginHeight
30176 onRotateLeft : function(e)
30178 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30180 var minScale = this.thumbEl.getWidth() / this.minWidth;
30182 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30183 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30185 this.startScale = this.scale;
30187 while (this.getScaleLevel() < minScale){
30189 this.scale = this.scale + 1;
30191 if(!this.zoomable()){
30196 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30197 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30202 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30209 this.scale = this.startScale;
30211 this.onRotateFail();
30216 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30218 if(this.isDocument){
30219 this.setThumbBoxSize();
30220 this.setThumbBoxPosition();
30221 this.setCanvasPosition();
30226 this.fireEvent('rotate', this, 'left');
30230 onRotateRight : function(e)
30232 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30234 var minScale = this.thumbEl.getWidth() / this.minWidth;
30236 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30237 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30239 this.startScale = this.scale;
30241 while (this.getScaleLevel() < minScale){
30243 this.scale = this.scale + 1;
30245 if(!this.zoomable()){
30250 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30251 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30256 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30263 this.scale = this.startScale;
30265 this.onRotateFail();
30270 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30272 if(this.isDocument){
30273 this.setThumbBoxSize();
30274 this.setThumbBoxPosition();
30275 this.setCanvasPosition();
30280 this.fireEvent('rotate', this, 'right');
30283 onRotateFail : function()
30285 this.errorEl.show(true);
30289 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30294 this.previewEl.dom.innerHTML = '';
30296 var canvasEl = document.createElement("canvas");
30298 var contextEl = canvasEl.getContext("2d");
30300 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30301 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30302 var center = this.imageEl.OriginWidth / 2;
30304 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30305 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30306 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30307 center = this.imageEl.OriginHeight / 2;
30310 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30312 contextEl.translate(center, center);
30313 contextEl.rotate(this.rotate * Math.PI / 180);
30315 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30317 this.canvasEl = document.createElement("canvas");
30319 this.contextEl = this.canvasEl.getContext("2d");
30321 switch (this.rotate) {
30324 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30325 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30327 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30332 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30333 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30335 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30336 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);
30340 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30345 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30346 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30348 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30349 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);
30353 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);
30358 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30359 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30361 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30362 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30366 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);
30373 this.previewEl.appendChild(this.canvasEl);
30375 this.setCanvasPosition();
30380 if(!this.canvasLoaded){
30384 var imageCanvas = document.createElement("canvas");
30386 var imageContext = imageCanvas.getContext("2d");
30388 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30389 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30391 var center = imageCanvas.width / 2;
30393 imageContext.translate(center, center);
30395 imageContext.rotate(this.rotate * Math.PI / 180);
30397 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30399 var canvas = document.createElement("canvas");
30401 var context = canvas.getContext("2d");
30403 canvas.width = this.minWidth;
30404 canvas.height = this.minHeight;
30406 switch (this.rotate) {
30409 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30410 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30412 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30413 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30415 var targetWidth = this.minWidth - 2 * x;
30416 var targetHeight = this.minHeight - 2 * y;
30420 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30421 scale = targetWidth / width;
30424 if(x > 0 && y == 0){
30425 scale = targetHeight / height;
30428 if(x > 0 && y > 0){
30429 scale = targetWidth / width;
30431 if(width < height){
30432 scale = targetHeight / height;
30436 context.scale(scale, scale);
30438 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30439 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30441 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30442 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30444 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30449 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30450 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30452 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30453 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30455 var targetWidth = this.minWidth - 2 * x;
30456 var targetHeight = this.minHeight - 2 * y;
30460 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30461 scale = targetWidth / width;
30464 if(x > 0 && y == 0){
30465 scale = targetHeight / height;
30468 if(x > 0 && y > 0){
30469 scale = targetWidth / width;
30471 if(width < height){
30472 scale = targetHeight / height;
30476 context.scale(scale, scale);
30478 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30479 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30481 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30482 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30484 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30486 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30491 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30492 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30494 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30495 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30497 var targetWidth = this.minWidth - 2 * x;
30498 var targetHeight = this.minHeight - 2 * y;
30502 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30503 scale = targetWidth / width;
30506 if(x > 0 && y == 0){
30507 scale = targetHeight / height;
30510 if(x > 0 && y > 0){
30511 scale = targetWidth / width;
30513 if(width < height){
30514 scale = targetHeight / height;
30518 context.scale(scale, scale);
30520 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30521 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30523 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30524 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30526 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30527 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30529 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30534 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30535 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30537 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30538 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30540 var targetWidth = this.minWidth - 2 * x;
30541 var targetHeight = this.minHeight - 2 * y;
30545 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30546 scale = targetWidth / width;
30549 if(x > 0 && y == 0){
30550 scale = targetHeight / height;
30553 if(x > 0 && y > 0){
30554 scale = targetWidth / width;
30556 if(width < height){
30557 scale = targetHeight / height;
30561 context.scale(scale, scale);
30563 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30564 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30566 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30567 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30569 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30571 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30578 this.cropData = canvas.toDataURL(this.cropType);
30580 if(this.fireEvent('crop', this, this.cropData) !== false){
30581 this.process(this.file, this.cropData);
30588 setThumbBoxSize : function()
30592 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30593 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30594 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30596 this.minWidth = width;
30597 this.minHeight = height;
30599 if(this.rotate == 90 || this.rotate == 270){
30600 this.minWidth = height;
30601 this.minHeight = width;
30606 width = Math.ceil(this.minWidth * height / this.minHeight);
30608 if(this.minWidth > this.minHeight){
30610 height = Math.ceil(this.minHeight * width / this.minWidth);
30613 this.thumbEl.setStyle({
30614 width : width + 'px',
30615 height : height + 'px'
30622 setThumbBoxPosition : function()
30624 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30625 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30627 this.thumbEl.setLeft(x);
30628 this.thumbEl.setTop(y);
30632 baseRotateLevel : function()
30634 this.baseRotate = 1;
30637 typeof(this.exif) != 'undefined' &&
30638 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30639 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30641 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30644 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30648 baseScaleLevel : function()
30652 if(this.isDocument){
30654 if(this.baseRotate == 6 || this.baseRotate == 8){
30656 height = this.thumbEl.getHeight();
30657 this.baseScale = height / this.imageEl.OriginWidth;
30659 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30660 width = this.thumbEl.getWidth();
30661 this.baseScale = width / this.imageEl.OriginHeight;
30667 height = this.thumbEl.getHeight();
30668 this.baseScale = height / this.imageEl.OriginHeight;
30670 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30671 width = this.thumbEl.getWidth();
30672 this.baseScale = width / this.imageEl.OriginWidth;
30678 if(this.baseRotate == 6 || this.baseRotate == 8){
30680 width = this.thumbEl.getHeight();
30681 this.baseScale = width / this.imageEl.OriginHeight;
30683 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30684 height = this.thumbEl.getWidth();
30685 this.baseScale = height / this.imageEl.OriginHeight;
30688 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30689 height = this.thumbEl.getWidth();
30690 this.baseScale = height / this.imageEl.OriginHeight;
30692 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30693 width = this.thumbEl.getHeight();
30694 this.baseScale = width / this.imageEl.OriginWidth;
30701 width = this.thumbEl.getWidth();
30702 this.baseScale = width / this.imageEl.OriginWidth;
30704 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30705 height = this.thumbEl.getHeight();
30706 this.baseScale = height / this.imageEl.OriginHeight;
30709 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30711 height = this.thumbEl.getHeight();
30712 this.baseScale = height / this.imageEl.OriginHeight;
30714 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30715 width = this.thumbEl.getWidth();
30716 this.baseScale = width / this.imageEl.OriginWidth;
30724 getScaleLevel : function()
30726 return this.baseScale * Math.pow(1.1, this.scale);
30729 onTouchStart : function(e)
30731 if(!this.canvasLoaded){
30732 this.beforeSelectFile(e);
30736 var touches = e.browserEvent.touches;
30742 if(touches.length == 1){
30743 this.onMouseDown(e);
30747 if(touches.length != 2){
30753 for(var i = 0, finger; finger = touches[i]; i++){
30754 coords.push(finger.pageX, finger.pageY);
30757 var x = Math.pow(coords[0] - coords[2], 2);
30758 var y = Math.pow(coords[1] - coords[3], 2);
30760 this.startDistance = Math.sqrt(x + y);
30762 this.startScale = this.scale;
30764 this.pinching = true;
30765 this.dragable = false;
30769 onTouchMove : function(e)
30771 if(!this.pinching && !this.dragable){
30775 var touches = e.browserEvent.touches;
30782 this.onMouseMove(e);
30788 for(var i = 0, finger; finger = touches[i]; i++){
30789 coords.push(finger.pageX, finger.pageY);
30792 var x = Math.pow(coords[0] - coords[2], 2);
30793 var y = Math.pow(coords[1] - coords[3], 2);
30795 this.endDistance = Math.sqrt(x + y);
30797 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30799 if(!this.zoomable()){
30800 this.scale = this.startScale;
30808 onTouchEnd : function(e)
30810 this.pinching = false;
30811 this.dragable = false;
30815 process : function(file, crop)
30818 this.maskEl.mask(this.loadingText);
30821 this.xhr = new XMLHttpRequest();
30823 file.xhr = this.xhr;
30825 this.xhr.open(this.method, this.url, true);
30828 "Accept": "application/json",
30829 "Cache-Control": "no-cache",
30830 "X-Requested-With": "XMLHttpRequest"
30833 for (var headerName in headers) {
30834 var headerValue = headers[headerName];
30836 this.xhr.setRequestHeader(headerName, headerValue);
30842 this.xhr.onload = function()
30844 _this.xhrOnLoad(_this.xhr);
30847 this.xhr.onerror = function()
30849 _this.xhrOnError(_this.xhr);
30852 var formData = new FormData();
30854 formData.append('returnHTML', 'NO');
30857 formData.append('crop', crop);
30860 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30861 formData.append(this.paramName, file, file.name);
30864 if(typeof(file.filename) != 'undefined'){
30865 formData.append('filename', file.filename);
30868 if(typeof(file.mimetype) != 'undefined'){
30869 formData.append('mimetype', file.mimetype);
30872 if(this.fireEvent('arrange', this, formData) != false){
30873 this.xhr.send(formData);
30877 xhrOnLoad : function(xhr)
30880 this.maskEl.unmask();
30883 if (xhr.readyState !== 4) {
30884 this.fireEvent('exception', this, xhr);
30888 var response = Roo.decode(xhr.responseText);
30890 if(!response.success){
30891 this.fireEvent('exception', this, xhr);
30895 var response = Roo.decode(xhr.responseText);
30897 this.fireEvent('upload', this, response);
30901 xhrOnError : function()
30904 this.maskEl.unmask();
30907 Roo.log('xhr on error');
30909 var response = Roo.decode(xhr.responseText);
30915 prepare : function(file)
30918 this.maskEl.mask(this.loadingText);
30924 if(typeof(file) === 'string'){
30925 this.loadCanvas(file);
30929 if(!file || !this.urlAPI){
30934 this.cropType = file.type;
30938 if(this.fireEvent('prepare', this, this.file) != false){
30940 var reader = new FileReader();
30942 reader.onload = function (e) {
30943 if (e.target.error) {
30944 Roo.log(e.target.error);
30948 var buffer = e.target.result,
30949 dataView = new DataView(buffer),
30951 maxOffset = dataView.byteLength - 4,
30955 if (dataView.getUint16(0) === 0xffd8) {
30956 while (offset < maxOffset) {
30957 markerBytes = dataView.getUint16(offset);
30959 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30960 markerLength = dataView.getUint16(offset + 2) + 2;
30961 if (offset + markerLength > dataView.byteLength) {
30962 Roo.log('Invalid meta data: Invalid segment size.');
30966 if(markerBytes == 0xffe1){
30967 _this.parseExifData(
30974 offset += markerLength;
30984 var url = _this.urlAPI.createObjectURL(_this.file);
30986 _this.loadCanvas(url);
30991 reader.readAsArrayBuffer(this.file);
30997 parseExifData : function(dataView, offset, length)
30999 var tiffOffset = offset + 10,
31003 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31004 // No Exif data, might be XMP data instead
31008 // Check for the ASCII code for "Exif" (0x45786966):
31009 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31010 // No Exif data, might be XMP data instead
31013 if (tiffOffset + 8 > dataView.byteLength) {
31014 Roo.log('Invalid Exif data: Invalid segment size.');
31017 // Check for the two null bytes:
31018 if (dataView.getUint16(offset + 8) !== 0x0000) {
31019 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31022 // Check the byte alignment:
31023 switch (dataView.getUint16(tiffOffset)) {
31025 littleEndian = true;
31028 littleEndian = false;
31031 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31034 // Check for the TIFF tag marker (0x002A):
31035 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31036 Roo.log('Invalid Exif data: Missing TIFF marker.');
31039 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31040 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31042 this.parseExifTags(
31045 tiffOffset + dirOffset,
31050 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31055 if (dirOffset + 6 > dataView.byteLength) {
31056 Roo.log('Invalid Exif data: Invalid directory offset.');
31059 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31060 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31061 if (dirEndOffset + 4 > dataView.byteLength) {
31062 Roo.log('Invalid Exif data: Invalid directory size.');
31065 for (i = 0; i < tagsNumber; i += 1) {
31069 dirOffset + 2 + 12 * i, // tag offset
31073 // Return the offset to the next directory:
31074 return dataView.getUint32(dirEndOffset, littleEndian);
31077 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31079 var tag = dataView.getUint16(offset, littleEndian);
31081 this.exif[tag] = this.getExifValue(
31085 dataView.getUint16(offset + 2, littleEndian), // tag type
31086 dataView.getUint32(offset + 4, littleEndian), // tag length
31091 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31093 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31102 Roo.log('Invalid Exif data: Invalid tag type.');
31106 tagSize = tagType.size * length;
31107 // Determine if the value is contained in the dataOffset bytes,
31108 // or if the value at the dataOffset is a pointer to the actual data:
31109 dataOffset = tagSize > 4 ?
31110 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31111 if (dataOffset + tagSize > dataView.byteLength) {
31112 Roo.log('Invalid Exif data: Invalid data offset.');
31115 if (length === 1) {
31116 return tagType.getValue(dataView, dataOffset, littleEndian);
31119 for (i = 0; i < length; i += 1) {
31120 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31123 if (tagType.ascii) {
31125 // Concatenate the chars:
31126 for (i = 0; i < values.length; i += 1) {
31128 // Ignore the terminating NULL byte(s):
31129 if (c === '\u0000') {
31141 Roo.apply(Roo.bootstrap.UploadCropbox, {
31143 'Orientation': 0x0112
31147 1: 0, //'top-left',
31149 3: 180, //'bottom-right',
31150 // 4: 'bottom-left',
31152 6: 90, //'right-top',
31153 // 7: 'right-bottom',
31154 8: 270 //'left-bottom'
31158 // byte, 8-bit unsigned int:
31160 getValue: function (dataView, dataOffset) {
31161 return dataView.getUint8(dataOffset);
31165 // ascii, 8-bit byte:
31167 getValue: function (dataView, dataOffset) {
31168 return String.fromCharCode(dataView.getUint8(dataOffset));
31173 // short, 16 bit int:
31175 getValue: function (dataView, dataOffset, littleEndian) {
31176 return dataView.getUint16(dataOffset, littleEndian);
31180 // long, 32 bit int:
31182 getValue: function (dataView, dataOffset, littleEndian) {
31183 return dataView.getUint32(dataOffset, littleEndian);
31187 // rational = two long values, first is numerator, second is denominator:
31189 getValue: function (dataView, dataOffset, littleEndian) {
31190 return dataView.getUint32(dataOffset, littleEndian) /
31191 dataView.getUint32(dataOffset + 4, littleEndian);
31195 // slong, 32 bit signed int:
31197 getValue: function (dataView, dataOffset, littleEndian) {
31198 return dataView.getInt32(dataOffset, littleEndian);
31202 // srational, two slongs, first is numerator, second is denominator:
31204 getValue: function (dataView, dataOffset, littleEndian) {
31205 return dataView.getInt32(dataOffset, littleEndian) /
31206 dataView.getInt32(dataOffset + 4, littleEndian);
31216 cls : 'btn-group roo-upload-cropbox-rotate-left',
31217 action : 'rotate-left',
31221 cls : 'btn btn-default',
31222 html : '<i class="fa fa-undo"></i>'
31228 cls : 'btn-group roo-upload-cropbox-picture',
31229 action : 'picture',
31233 cls : 'btn btn-default',
31234 html : '<i class="fa fa-picture-o"></i>'
31240 cls : 'btn-group roo-upload-cropbox-rotate-right',
31241 action : 'rotate-right',
31245 cls : 'btn btn-default',
31246 html : '<i class="fa fa-repeat"></i>'
31254 cls : 'btn-group roo-upload-cropbox-rotate-left',
31255 action : 'rotate-left',
31259 cls : 'btn btn-default',
31260 html : '<i class="fa fa-undo"></i>'
31266 cls : 'btn-group roo-upload-cropbox-download',
31267 action : 'download',
31271 cls : 'btn btn-default',
31272 html : '<i class="fa fa-download"></i>'
31278 cls : 'btn-group roo-upload-cropbox-crop',
31283 cls : 'btn btn-default',
31284 html : '<i class="fa fa-crop"></i>'
31290 cls : 'btn-group roo-upload-cropbox-trash',
31295 cls : 'btn btn-default',
31296 html : '<i class="fa fa-trash"></i>'
31302 cls : 'btn-group roo-upload-cropbox-rotate-right',
31303 action : 'rotate-right',
31307 cls : 'btn btn-default',
31308 html : '<i class="fa fa-repeat"></i>'
31316 cls : 'btn-group roo-upload-cropbox-rotate-left',
31317 action : 'rotate-left',
31321 cls : 'btn btn-default',
31322 html : '<i class="fa fa-undo"></i>'
31328 cls : 'btn-group roo-upload-cropbox-rotate-right',
31329 action : 'rotate-right',
31333 cls : 'btn btn-default',
31334 html : '<i class="fa fa-repeat"></i>'
31347 * @class Roo.bootstrap.DocumentManager
31348 * @extends Roo.bootstrap.Component
31349 * Bootstrap DocumentManager class
31350 * @cfg {String} paramName default 'imageUpload'
31351 * @cfg {String} toolTipName default 'filename'
31352 * @cfg {String} method default POST
31353 * @cfg {String} url action url
31354 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31355 * @cfg {Boolean} multiple multiple upload default true
31356 * @cfg {Number} thumbSize default 300
31357 * @cfg {String} fieldLabel
31358 * @cfg {Number} labelWidth default 4
31359 * @cfg {String} labelAlign (left|top) default left
31360 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31361 * @cfg {Number} labellg set the width of label (1-12)
31362 * @cfg {Number} labelmd set the width of label (1-12)
31363 * @cfg {Number} labelsm set the width of label (1-12)
31364 * @cfg {Number} labelxs set the width of label (1-12)
31367 * Create a new DocumentManager
31368 * @param {Object} config The config object
31371 Roo.bootstrap.DocumentManager = function(config){
31372 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31375 this.delegates = [];
31380 * Fire when initial the DocumentManager
31381 * @param {Roo.bootstrap.DocumentManager} this
31386 * inspect selected file
31387 * @param {Roo.bootstrap.DocumentManager} this
31388 * @param {File} file
31393 * Fire when xhr load exception
31394 * @param {Roo.bootstrap.DocumentManager} this
31395 * @param {XMLHttpRequest} xhr
31397 "exception" : true,
31399 * @event afterupload
31400 * Fire when xhr load exception
31401 * @param {Roo.bootstrap.DocumentManager} this
31402 * @param {XMLHttpRequest} xhr
31404 "afterupload" : true,
31407 * prepare the form data
31408 * @param {Roo.bootstrap.DocumentManager} this
31409 * @param {Object} formData
31414 * Fire when remove the file
31415 * @param {Roo.bootstrap.DocumentManager} this
31416 * @param {Object} file
31421 * Fire after refresh the file
31422 * @param {Roo.bootstrap.DocumentManager} this
31427 * Fire after click the image
31428 * @param {Roo.bootstrap.DocumentManager} this
31429 * @param {Object} file
31434 * Fire when upload a image and editable set to true
31435 * @param {Roo.bootstrap.DocumentManager} this
31436 * @param {Object} file
31440 * @event beforeselectfile
31441 * Fire before select file
31442 * @param {Roo.bootstrap.DocumentManager} this
31444 "beforeselectfile" : true,
31447 * Fire before process file
31448 * @param {Roo.bootstrap.DocumentManager} this
31449 * @param {Object} file
31453 * @event previewrendered
31454 * Fire when preview rendered
31455 * @param {Roo.bootstrap.DocumentManager} this
31456 * @param {Object} file
31458 "previewrendered" : true,
31461 "previewResize" : true
31466 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31475 paramName : 'imageUpload',
31476 toolTipName : 'filename',
31479 labelAlign : 'left',
31489 getAutoCreate : function()
31491 var managerWidget = {
31493 cls : 'roo-document-manager',
31497 cls : 'roo-document-manager-selector',
31502 cls : 'roo-document-manager-uploader',
31506 cls : 'roo-document-manager-upload-btn',
31507 html : '<i class="fa fa-plus"></i>'
31518 cls : 'column col-md-12',
31523 if(this.fieldLabel.length){
31528 cls : 'column col-md-12',
31529 html : this.fieldLabel
31533 cls : 'column col-md-12',
31538 if(this.labelAlign == 'left'){
31543 html : this.fieldLabel
31552 if(this.labelWidth > 12){
31553 content[0].style = "width: " + this.labelWidth + 'px';
31556 if(this.labelWidth < 13 && this.labelmd == 0){
31557 this.labelmd = this.labelWidth;
31560 if(this.labellg > 0){
31561 content[0].cls += ' col-lg-' + this.labellg;
31562 content[1].cls += ' col-lg-' + (12 - this.labellg);
31565 if(this.labelmd > 0){
31566 content[0].cls += ' col-md-' + this.labelmd;
31567 content[1].cls += ' col-md-' + (12 - this.labelmd);
31570 if(this.labelsm > 0){
31571 content[0].cls += ' col-sm-' + this.labelsm;
31572 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31575 if(this.labelxs > 0){
31576 content[0].cls += ' col-xs-' + this.labelxs;
31577 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31585 cls : 'row clearfix',
31593 initEvents : function()
31595 this.managerEl = this.el.select('.roo-document-manager', true).first();
31596 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31598 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31599 this.selectorEl.hide();
31602 this.selectorEl.attr('multiple', 'multiple');
31605 this.selectorEl.on('change', this.onFileSelected, this);
31607 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31608 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31610 this.uploader.on('click', this.onUploaderClick, this);
31612 this.renderProgressDialog();
31616 window.addEventListener("resize", function() { _this.refresh(); } );
31618 this.fireEvent('initial', this);
31621 renderProgressDialog : function()
31625 this.progressDialog = new Roo.bootstrap.Modal({
31626 cls : 'roo-document-manager-progress-dialog',
31627 allow_close : false,
31638 btnclick : function() {
31639 _this.uploadCancel();
31645 this.progressDialog.render(Roo.get(document.body));
31647 this.progress = new Roo.bootstrap.Progress({
31648 cls : 'roo-document-manager-progress',
31653 this.progress.render(this.progressDialog.getChildContainer());
31655 this.progressBar = new Roo.bootstrap.ProgressBar({
31656 cls : 'roo-document-manager-progress-bar',
31659 aria_valuemax : 12,
31663 this.progressBar.render(this.progress.getChildContainer());
31666 onUploaderClick : function(e)
31668 e.preventDefault();
31670 if(this.fireEvent('beforeselectfile', this) != false){
31671 this.selectorEl.dom.click();
31676 onFileSelected : function(e)
31678 e.preventDefault();
31680 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31684 Roo.each(this.selectorEl.dom.files, function(file){
31685 if(this.fireEvent('inspect', this, file) != false){
31686 this.files.push(file);
31696 this.selectorEl.dom.value = '';
31698 if(!this.files || !this.files.length){
31702 if(this.boxes > 0 && this.files.length > this.boxes){
31703 this.files = this.files.slice(0, this.boxes);
31706 this.uploader.show();
31708 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31709 this.uploader.hide();
31718 Roo.each(this.files, function(file){
31720 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31721 var f = this.renderPreview(file);
31726 if(file.type.indexOf('image') != -1){
31727 this.delegates.push(
31729 _this.process(file);
31730 }).createDelegate(this)
31738 _this.process(file);
31739 }).createDelegate(this)
31744 this.files = files;
31746 this.delegates = this.delegates.concat(docs);
31748 if(!this.delegates.length){
31753 this.progressBar.aria_valuemax = this.delegates.length;
31760 arrange : function()
31762 if(!this.delegates.length){
31763 this.progressDialog.hide();
31768 var delegate = this.delegates.shift();
31770 this.progressDialog.show();
31772 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31774 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31779 refresh : function()
31781 this.uploader.show();
31783 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31784 this.uploader.hide();
31787 Roo.isTouch ? this.closable(false) : this.closable(true);
31789 this.fireEvent('refresh', this);
31792 onRemove : function(e, el, o)
31794 e.preventDefault();
31796 this.fireEvent('remove', this, o);
31800 remove : function(o)
31804 Roo.each(this.files, function(file){
31805 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31814 this.files = files;
31821 Roo.each(this.files, function(file){
31826 file.target.remove();
31835 onClick : function(e, el, o)
31837 e.preventDefault();
31839 this.fireEvent('click', this, o);
31843 closable : function(closable)
31845 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31847 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31859 xhrOnLoad : function(xhr)
31861 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31865 if (xhr.readyState !== 4) {
31867 this.fireEvent('exception', this, xhr);
31871 var response = Roo.decode(xhr.responseText);
31873 if(!response.success){
31875 this.fireEvent('exception', this, xhr);
31879 var file = this.renderPreview(response.data);
31881 this.files.push(file);
31885 this.fireEvent('afterupload', this, xhr);
31889 xhrOnError : function(xhr)
31891 Roo.log('xhr on error');
31893 var response = Roo.decode(xhr.responseText);
31900 process : function(file)
31902 if(this.fireEvent('process', this, file) !== false){
31903 if(this.editable && file.type.indexOf('image') != -1){
31904 this.fireEvent('edit', this, file);
31908 this.uploadStart(file, false);
31915 uploadStart : function(file, crop)
31917 this.xhr = new XMLHttpRequest();
31919 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31924 file.xhr = this.xhr;
31926 this.managerEl.createChild({
31928 cls : 'roo-document-manager-loading',
31932 tooltip : file.name,
31933 cls : 'roo-document-manager-thumb',
31934 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31940 this.xhr.open(this.method, this.url, true);
31943 "Accept": "application/json",
31944 "Cache-Control": "no-cache",
31945 "X-Requested-With": "XMLHttpRequest"
31948 for (var headerName in headers) {
31949 var headerValue = headers[headerName];
31951 this.xhr.setRequestHeader(headerName, headerValue);
31957 this.xhr.onload = function()
31959 _this.xhrOnLoad(_this.xhr);
31962 this.xhr.onerror = function()
31964 _this.xhrOnError(_this.xhr);
31967 var formData = new FormData();
31969 formData.append('returnHTML', 'NO');
31972 formData.append('crop', crop);
31975 formData.append(this.paramName, file, file.name);
31982 if(this.fireEvent('prepare', this, formData, options) != false){
31984 if(options.manually){
31988 this.xhr.send(formData);
31992 this.uploadCancel();
31995 uploadCancel : function()
32001 this.delegates = [];
32003 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32010 renderPreview : function(file)
32012 if(typeof(file.target) != 'undefined' && file.target){
32016 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32018 var previewEl = this.managerEl.createChild({
32020 cls : 'roo-document-manager-preview',
32024 tooltip : file[this.toolTipName],
32025 cls : 'roo-document-manager-thumb',
32026 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32031 html : '<i class="fa fa-times-circle"></i>'
32036 var close = previewEl.select('button.close', true).first();
32038 close.on('click', this.onRemove, this, file);
32040 file.target = previewEl;
32042 var image = previewEl.select('img', true).first();
32046 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32048 image.on('click', this.onClick, this, file);
32050 this.fireEvent('previewrendered', this, file);
32056 onPreviewLoad : function(file, image)
32058 if(typeof(file.target) == 'undefined' || !file.target){
32062 var width = image.dom.naturalWidth || image.dom.width;
32063 var height = image.dom.naturalHeight || image.dom.height;
32065 if(!this.previewResize) {
32069 if(width > height){
32070 file.target.addClass('wide');
32074 file.target.addClass('tall');
32079 uploadFromSource : function(file, crop)
32081 this.xhr = new XMLHttpRequest();
32083 this.managerEl.createChild({
32085 cls : 'roo-document-manager-loading',
32089 tooltip : file.name,
32090 cls : 'roo-document-manager-thumb',
32091 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32097 this.xhr.open(this.method, this.url, true);
32100 "Accept": "application/json",
32101 "Cache-Control": "no-cache",
32102 "X-Requested-With": "XMLHttpRequest"
32105 for (var headerName in headers) {
32106 var headerValue = headers[headerName];
32108 this.xhr.setRequestHeader(headerName, headerValue);
32114 this.xhr.onload = function()
32116 _this.xhrOnLoad(_this.xhr);
32119 this.xhr.onerror = function()
32121 _this.xhrOnError(_this.xhr);
32124 var formData = new FormData();
32126 formData.append('returnHTML', 'NO');
32128 formData.append('crop', crop);
32130 if(typeof(file.filename) != 'undefined'){
32131 formData.append('filename', file.filename);
32134 if(typeof(file.mimetype) != 'undefined'){
32135 formData.append('mimetype', file.mimetype);
32140 if(this.fireEvent('prepare', this, formData) != false){
32141 this.xhr.send(formData);
32151 * @class Roo.bootstrap.DocumentViewer
32152 * @extends Roo.bootstrap.Component
32153 * Bootstrap DocumentViewer class
32154 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32155 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32158 * Create a new DocumentViewer
32159 * @param {Object} config The config object
32162 Roo.bootstrap.DocumentViewer = function(config){
32163 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32168 * Fire after initEvent
32169 * @param {Roo.bootstrap.DocumentViewer} this
32175 * @param {Roo.bootstrap.DocumentViewer} this
32180 * Fire after download button
32181 * @param {Roo.bootstrap.DocumentViewer} this
32186 * Fire after trash button
32187 * @param {Roo.bootstrap.DocumentViewer} this
32194 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32196 showDownload : true,
32200 getAutoCreate : function()
32204 cls : 'roo-document-viewer',
32208 cls : 'roo-document-viewer-body',
32212 cls : 'roo-document-viewer-thumb',
32216 cls : 'roo-document-viewer-image'
32224 cls : 'roo-document-viewer-footer',
32227 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32231 cls : 'btn-group roo-document-viewer-download',
32235 cls : 'btn btn-default',
32236 html : '<i class="fa fa-download"></i>'
32242 cls : 'btn-group roo-document-viewer-trash',
32246 cls : 'btn btn-default',
32247 html : '<i class="fa fa-trash"></i>'
32260 initEvents : function()
32262 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32263 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32265 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32266 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32268 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32269 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32271 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32272 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32274 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32275 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32277 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32278 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32280 this.bodyEl.on('click', this.onClick, this);
32281 this.downloadBtn.on('click', this.onDownload, this);
32282 this.trashBtn.on('click', this.onTrash, this);
32284 this.downloadBtn.hide();
32285 this.trashBtn.hide();
32287 if(this.showDownload){
32288 this.downloadBtn.show();
32291 if(this.showTrash){
32292 this.trashBtn.show();
32295 if(!this.showDownload && !this.showTrash) {
32296 this.footerEl.hide();
32301 initial : function()
32303 this.fireEvent('initial', this);
32307 onClick : function(e)
32309 e.preventDefault();
32311 this.fireEvent('click', this);
32314 onDownload : function(e)
32316 e.preventDefault();
32318 this.fireEvent('download', this);
32321 onTrash : function(e)
32323 e.preventDefault();
32325 this.fireEvent('trash', this);
32337 * @class Roo.bootstrap.NavProgressBar
32338 * @extends Roo.bootstrap.Component
32339 * Bootstrap NavProgressBar class
32342 * Create a new nav progress bar
32343 * @param {Object} config The config object
32346 Roo.bootstrap.NavProgressBar = function(config){
32347 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32349 this.bullets = this.bullets || [];
32351 // Roo.bootstrap.NavProgressBar.register(this);
32355 * Fires when the active item changes
32356 * @param {Roo.bootstrap.NavProgressBar} this
32357 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32358 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32365 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32370 getAutoCreate : function()
32372 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32376 cls : 'roo-navigation-bar-group',
32380 cls : 'roo-navigation-top-bar'
32384 cls : 'roo-navigation-bullets-bar',
32388 cls : 'roo-navigation-bar'
32395 cls : 'roo-navigation-bottom-bar'
32405 initEvents: function()
32410 onRender : function(ct, position)
32412 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32414 if(this.bullets.length){
32415 Roo.each(this.bullets, function(b){
32424 addItem : function(cfg)
32426 var item = new Roo.bootstrap.NavProgressItem(cfg);
32428 item.parentId = this.id;
32429 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32432 var top = new Roo.bootstrap.Element({
32434 cls : 'roo-navigation-bar-text'
32437 var bottom = new Roo.bootstrap.Element({
32439 cls : 'roo-navigation-bar-text'
32442 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32443 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32445 var topText = new Roo.bootstrap.Element({
32447 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32450 var bottomText = new Roo.bootstrap.Element({
32452 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32455 topText.onRender(top.el, null);
32456 bottomText.onRender(bottom.el, null);
32459 item.bottomEl = bottom;
32462 this.barItems.push(item);
32467 getActive : function()
32469 var active = false;
32471 Roo.each(this.barItems, function(v){
32473 if (!v.isActive()) {
32485 setActiveItem : function(item)
32489 Roo.each(this.barItems, function(v){
32490 if (v.rid == item.rid) {
32494 if (v.isActive()) {
32495 v.setActive(false);
32500 item.setActive(true);
32502 this.fireEvent('changed', this, item, prev);
32505 getBarItem: function(rid)
32509 Roo.each(this.barItems, function(e) {
32510 if (e.rid != rid) {
32521 indexOfItem : function(item)
32525 Roo.each(this.barItems, function(v, i){
32527 if (v.rid != item.rid) {
32538 setActiveNext : function()
32540 var i = this.indexOfItem(this.getActive());
32542 if (i > this.barItems.length) {
32546 this.setActiveItem(this.barItems[i+1]);
32549 setActivePrev : function()
32551 var i = this.indexOfItem(this.getActive());
32557 this.setActiveItem(this.barItems[i-1]);
32560 format : function()
32562 if(!this.barItems.length){
32566 var width = 100 / this.barItems.length;
32568 Roo.each(this.barItems, function(i){
32569 i.el.setStyle('width', width + '%');
32570 i.topEl.el.setStyle('width', width + '%');
32571 i.bottomEl.el.setStyle('width', width + '%');
32580 * Nav Progress Item
32585 * @class Roo.bootstrap.NavProgressItem
32586 * @extends Roo.bootstrap.Component
32587 * Bootstrap NavProgressItem class
32588 * @cfg {String} rid the reference id
32589 * @cfg {Boolean} active (true|false) Is item active default false
32590 * @cfg {Boolean} disabled (true|false) Is item active default false
32591 * @cfg {String} html
32592 * @cfg {String} position (top|bottom) text position default bottom
32593 * @cfg {String} icon show icon instead of number
32596 * Create a new NavProgressItem
32597 * @param {Object} config The config object
32599 Roo.bootstrap.NavProgressItem = function(config){
32600 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32605 * The raw click event for the entire grid.
32606 * @param {Roo.bootstrap.NavProgressItem} this
32607 * @param {Roo.EventObject} e
32614 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32620 position : 'bottom',
32623 getAutoCreate : function()
32625 var iconCls = 'roo-navigation-bar-item-icon';
32627 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32631 cls: 'roo-navigation-bar-item',
32641 cfg.cls += ' active';
32644 cfg.cls += ' disabled';
32650 disable : function()
32652 this.setDisabled(true);
32655 enable : function()
32657 this.setDisabled(false);
32660 initEvents: function()
32662 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32664 this.iconEl.on('click', this.onClick, this);
32667 onClick : function(e)
32669 e.preventDefault();
32675 if(this.fireEvent('click', this, e) === false){
32679 this.parent().setActiveItem(this);
32682 isActive: function ()
32684 return this.active;
32687 setActive : function(state)
32689 if(this.active == state){
32693 this.active = state;
32696 this.el.addClass('active');
32700 this.el.removeClass('active');
32705 setDisabled : function(state)
32707 if(this.disabled == state){
32711 this.disabled = state;
32714 this.el.addClass('disabled');
32718 this.el.removeClass('disabled');
32721 tooltipEl : function()
32723 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32736 * @class Roo.bootstrap.FieldLabel
32737 * @extends Roo.bootstrap.Component
32738 * Bootstrap FieldLabel class
32739 * @cfg {String} html contents of the element
32740 * @cfg {String} tag tag of the element default label
32741 * @cfg {String} cls class of the element
32742 * @cfg {String} target label target
32743 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32744 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32745 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32746 * @cfg {String} iconTooltip default "This field is required"
32747 * @cfg {String} indicatorpos (left|right) default left
32750 * Create a new FieldLabel
32751 * @param {Object} config The config object
32754 Roo.bootstrap.FieldLabel = function(config){
32755 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32760 * Fires after the field has been marked as invalid.
32761 * @param {Roo.form.FieldLabel} this
32762 * @param {String} msg The validation message
32767 * Fires after the field has been validated with no errors.
32768 * @param {Roo.form.FieldLabel} this
32774 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32781 invalidClass : 'has-warning',
32782 validClass : 'has-success',
32783 iconTooltip : 'This field is required',
32784 indicatorpos : 'left',
32786 getAutoCreate : function(){
32789 if (!this.allowBlank) {
32795 cls : 'roo-bootstrap-field-label ' + this.cls,
32800 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32801 tooltip : this.iconTooltip
32810 if(this.indicatorpos == 'right'){
32813 cls : 'roo-bootstrap-field-label ' + this.cls,
32822 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32823 tooltip : this.iconTooltip
32832 initEvents: function()
32834 Roo.bootstrap.Element.superclass.initEvents.call(this);
32836 this.indicator = this.indicatorEl();
32838 if(this.indicator){
32839 this.indicator.removeClass('visible');
32840 this.indicator.addClass('invisible');
32843 Roo.bootstrap.FieldLabel.register(this);
32846 indicatorEl : function()
32848 var indicator = this.el.select('i.roo-required-indicator',true).first();
32859 * Mark this field as valid
32861 markValid : function()
32863 if(this.indicator){
32864 this.indicator.removeClass('visible');
32865 this.indicator.addClass('invisible');
32867 if (Roo.bootstrap.version == 3) {
32868 this.el.removeClass(this.invalidClass);
32869 this.el.addClass(this.validClass);
32871 this.el.removeClass('is-invalid');
32872 this.el.addClass('is-valid');
32876 this.fireEvent('valid', this);
32880 * Mark this field as invalid
32881 * @param {String} msg The validation message
32883 markInvalid : function(msg)
32885 if(this.indicator){
32886 this.indicator.removeClass('invisible');
32887 this.indicator.addClass('visible');
32889 if (Roo.bootstrap.version == 3) {
32890 this.el.removeClass(this.validClass);
32891 this.el.addClass(this.invalidClass);
32893 this.el.removeClass('is-valid');
32894 this.el.addClass('is-invalid');
32898 this.fireEvent('invalid', this, msg);
32904 Roo.apply(Roo.bootstrap.FieldLabel, {
32909 * register a FieldLabel Group
32910 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32912 register : function(label)
32914 if(this.groups.hasOwnProperty(label.target)){
32918 this.groups[label.target] = label;
32922 * fetch a FieldLabel Group based on the target
32923 * @param {string} target
32924 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32926 get: function(target) {
32927 if (typeof(this.groups[target]) == 'undefined') {
32931 return this.groups[target] ;
32940 * page DateSplitField.
32946 * @class Roo.bootstrap.DateSplitField
32947 * @extends Roo.bootstrap.Component
32948 * Bootstrap DateSplitField class
32949 * @cfg {string} fieldLabel - the label associated
32950 * @cfg {Number} labelWidth set the width of label (0-12)
32951 * @cfg {String} labelAlign (top|left)
32952 * @cfg {Boolean} dayAllowBlank (true|false) default false
32953 * @cfg {Boolean} monthAllowBlank (true|false) default false
32954 * @cfg {Boolean} yearAllowBlank (true|false) default false
32955 * @cfg {string} dayPlaceholder
32956 * @cfg {string} monthPlaceholder
32957 * @cfg {string} yearPlaceholder
32958 * @cfg {string} dayFormat default 'd'
32959 * @cfg {string} monthFormat default 'm'
32960 * @cfg {string} yearFormat default 'Y'
32961 * @cfg {Number} labellg set the width of label (1-12)
32962 * @cfg {Number} labelmd set the width of label (1-12)
32963 * @cfg {Number} labelsm set the width of label (1-12)
32964 * @cfg {Number} labelxs set the width of label (1-12)
32968 * Create a new DateSplitField
32969 * @param {Object} config The config object
32972 Roo.bootstrap.DateSplitField = function(config){
32973 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32979 * getting the data of years
32980 * @param {Roo.bootstrap.DateSplitField} this
32981 * @param {Object} years
32986 * getting the data of days
32987 * @param {Roo.bootstrap.DateSplitField} this
32988 * @param {Object} days
32993 * Fires after the field has been marked as invalid.
32994 * @param {Roo.form.Field} this
32995 * @param {String} msg The validation message
33000 * Fires after the field has been validated with no errors.
33001 * @param {Roo.form.Field} this
33007 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33010 labelAlign : 'top',
33012 dayAllowBlank : false,
33013 monthAllowBlank : false,
33014 yearAllowBlank : false,
33015 dayPlaceholder : '',
33016 monthPlaceholder : '',
33017 yearPlaceholder : '',
33021 isFormField : true,
33027 getAutoCreate : function()
33031 cls : 'row roo-date-split-field-group',
33036 cls : 'form-hidden-field roo-date-split-field-group-value',
33042 var labelCls = 'col-md-12';
33043 var contentCls = 'col-md-4';
33045 if(this.fieldLabel){
33049 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33053 html : this.fieldLabel
33058 if(this.labelAlign == 'left'){
33060 if(this.labelWidth > 12){
33061 label.style = "width: " + this.labelWidth + 'px';
33064 if(this.labelWidth < 13 && this.labelmd == 0){
33065 this.labelmd = this.labelWidth;
33068 if(this.labellg > 0){
33069 labelCls = ' col-lg-' + this.labellg;
33070 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33073 if(this.labelmd > 0){
33074 labelCls = ' col-md-' + this.labelmd;
33075 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33078 if(this.labelsm > 0){
33079 labelCls = ' col-sm-' + this.labelsm;
33080 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33083 if(this.labelxs > 0){
33084 labelCls = ' col-xs-' + this.labelxs;
33085 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33089 label.cls += ' ' + labelCls;
33091 cfg.cn.push(label);
33094 Roo.each(['day', 'month', 'year'], function(t){
33097 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33104 inputEl: function ()
33106 return this.el.select('.roo-date-split-field-group-value', true).first();
33109 onRender : function(ct, position)
33113 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33115 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33117 this.dayField = new Roo.bootstrap.ComboBox({
33118 allowBlank : this.dayAllowBlank,
33119 alwaysQuery : true,
33120 displayField : 'value',
33123 forceSelection : true,
33125 placeholder : this.dayPlaceholder,
33126 selectOnFocus : true,
33127 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33128 triggerAction : 'all',
33130 valueField : 'value',
33131 store : new Roo.data.SimpleStore({
33132 data : (function() {
33134 _this.fireEvent('days', _this, days);
33137 fields : [ 'value' ]
33140 select : function (_self, record, index)
33142 _this.setValue(_this.getValue());
33147 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33149 this.monthField = new Roo.bootstrap.MonthField({
33150 after : '<i class=\"fa fa-calendar\"></i>',
33151 allowBlank : this.monthAllowBlank,
33152 placeholder : this.monthPlaceholder,
33155 render : function (_self)
33157 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33158 e.preventDefault();
33162 select : function (_self, oldvalue, newvalue)
33164 _this.setValue(_this.getValue());
33169 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33171 this.yearField = new Roo.bootstrap.ComboBox({
33172 allowBlank : this.yearAllowBlank,
33173 alwaysQuery : true,
33174 displayField : 'value',
33177 forceSelection : true,
33179 placeholder : this.yearPlaceholder,
33180 selectOnFocus : true,
33181 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33182 triggerAction : 'all',
33184 valueField : 'value',
33185 store : new Roo.data.SimpleStore({
33186 data : (function() {
33188 _this.fireEvent('years', _this, years);
33191 fields : [ 'value' ]
33194 select : function (_self, record, index)
33196 _this.setValue(_this.getValue());
33201 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33204 setValue : function(v, format)
33206 this.inputEl.dom.value = v;
33208 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33210 var d = Date.parseDate(v, f);
33217 this.setDay(d.format(this.dayFormat));
33218 this.setMonth(d.format(this.monthFormat));
33219 this.setYear(d.format(this.yearFormat));
33226 setDay : function(v)
33228 this.dayField.setValue(v);
33229 this.inputEl.dom.value = this.getValue();
33234 setMonth : function(v)
33236 this.monthField.setValue(v, true);
33237 this.inputEl.dom.value = this.getValue();
33242 setYear : function(v)
33244 this.yearField.setValue(v);
33245 this.inputEl.dom.value = this.getValue();
33250 getDay : function()
33252 return this.dayField.getValue();
33255 getMonth : function()
33257 return this.monthField.getValue();
33260 getYear : function()
33262 return this.yearField.getValue();
33265 getValue : function()
33267 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33269 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33279 this.inputEl.dom.value = '';
33284 validate : function()
33286 var d = this.dayField.validate();
33287 var m = this.monthField.validate();
33288 var y = this.yearField.validate();
33293 (!this.dayAllowBlank && !d) ||
33294 (!this.monthAllowBlank && !m) ||
33295 (!this.yearAllowBlank && !y)
33300 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33309 this.markInvalid();
33314 markValid : function()
33317 var label = this.el.select('label', true).first();
33318 var icon = this.el.select('i.fa-star', true).first();
33324 this.fireEvent('valid', this);
33328 * Mark this field as invalid
33329 * @param {String} msg The validation message
33331 markInvalid : function(msg)
33334 var label = this.el.select('label', true).first();
33335 var icon = this.el.select('i.fa-star', true).first();
33337 if(label && !icon){
33338 this.el.select('.roo-date-split-field-label', true).createChild({
33340 cls : 'text-danger fa fa-lg fa-star',
33341 tooltip : 'This field is required',
33342 style : 'margin-right:5px;'
33346 this.fireEvent('invalid', this, msg);
33349 clearInvalid : function()
33351 var label = this.el.select('label', true).first();
33352 var icon = this.el.select('i.fa-star', true).first();
33358 this.fireEvent('valid', this);
33361 getName: function()
33371 * http://masonry.desandro.com
33373 * The idea is to render all the bricks based on vertical width...
33375 * The original code extends 'outlayer' - we might need to use that....
33381 * @class Roo.bootstrap.LayoutMasonry
33382 * @extends Roo.bootstrap.Component
33383 * Bootstrap Layout Masonry class
33386 * Create a new Element
33387 * @param {Object} config The config object
33390 Roo.bootstrap.LayoutMasonry = function(config){
33392 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33396 Roo.bootstrap.LayoutMasonry.register(this);
33402 * Fire after layout the items
33403 * @param {Roo.bootstrap.LayoutMasonry} this
33404 * @param {Roo.EventObject} e
33411 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33414 * @cfg {Boolean} isLayoutInstant = no animation?
33416 isLayoutInstant : false, // needed?
33419 * @cfg {Number} boxWidth width of the columns
33424 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33429 * @cfg {Number} padWidth padding below box..
33434 * @cfg {Number} gutter gutter width..
33439 * @cfg {Number} maxCols maximum number of columns
33445 * @cfg {Boolean} isAutoInitial defalut true
33447 isAutoInitial : true,
33452 * @cfg {Boolean} isHorizontal defalut false
33454 isHorizontal : false,
33456 currentSize : null,
33462 bricks: null, //CompositeElement
33466 _isLayoutInited : false,
33468 // isAlternative : false, // only use for vertical layout...
33471 * @cfg {Number} alternativePadWidth padding below box..
33473 alternativePadWidth : 50,
33475 selectedBrick : [],
33477 getAutoCreate : function(){
33479 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33483 cls: 'blog-masonary-wrapper ' + this.cls,
33485 cls : 'mas-boxes masonary'
33492 getChildContainer: function( )
33494 if (this.boxesEl) {
33495 return this.boxesEl;
33498 this.boxesEl = this.el.select('.mas-boxes').first();
33500 return this.boxesEl;
33504 initEvents : function()
33508 if(this.isAutoInitial){
33509 Roo.log('hook children rendered');
33510 this.on('childrenrendered', function() {
33511 Roo.log('children rendered');
33517 initial : function()
33519 this.selectedBrick = [];
33521 this.currentSize = this.el.getBox(true);
33523 Roo.EventManager.onWindowResize(this.resize, this);
33525 if(!this.isAutoInitial){
33533 //this.layout.defer(500,this);
33537 resize : function()
33539 var cs = this.el.getBox(true);
33542 this.currentSize.width == cs.width &&
33543 this.currentSize.x == cs.x &&
33544 this.currentSize.height == cs.height &&
33545 this.currentSize.y == cs.y
33547 Roo.log("no change in with or X or Y");
33551 this.currentSize = cs;
33557 layout : function()
33559 this._resetLayout();
33561 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33563 this.layoutItems( isInstant );
33565 this._isLayoutInited = true;
33567 this.fireEvent('layout', this);
33571 _resetLayout : function()
33573 if(this.isHorizontal){
33574 this.horizontalMeasureColumns();
33578 this.verticalMeasureColumns();
33582 verticalMeasureColumns : function()
33584 this.getContainerWidth();
33586 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33587 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33591 var boxWidth = this.boxWidth + this.padWidth;
33593 if(this.containerWidth < this.boxWidth){
33594 boxWidth = this.containerWidth
33597 var containerWidth = this.containerWidth;
33599 var cols = Math.floor(containerWidth / boxWidth);
33601 this.cols = Math.max( cols, 1 );
33603 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33605 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33607 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33609 this.colWidth = boxWidth + avail - this.padWidth;
33611 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33612 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33615 horizontalMeasureColumns : function()
33617 this.getContainerWidth();
33619 var boxWidth = this.boxWidth;
33621 if(this.containerWidth < boxWidth){
33622 boxWidth = this.containerWidth;
33625 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33627 this.el.setHeight(boxWidth);
33631 getContainerWidth : function()
33633 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33636 layoutItems : function( isInstant )
33638 Roo.log(this.bricks);
33640 var items = Roo.apply([], this.bricks);
33642 if(this.isHorizontal){
33643 this._horizontalLayoutItems( items , isInstant );
33647 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33648 // this._verticalAlternativeLayoutItems( items , isInstant );
33652 this._verticalLayoutItems( items , isInstant );
33656 _verticalLayoutItems : function ( items , isInstant)
33658 if ( !items || !items.length ) {
33663 ['xs', 'xs', 'xs', 'tall'],
33664 ['xs', 'xs', 'tall'],
33665 ['xs', 'xs', 'sm'],
33666 ['xs', 'xs', 'xs'],
33672 ['sm', 'xs', 'xs'],
33676 ['tall', 'xs', 'xs', 'xs'],
33677 ['tall', 'xs', 'xs'],
33689 Roo.each(items, function(item, k){
33691 switch (item.size) {
33692 // these layouts take up a full box,
33703 boxes.push([item]);
33726 var filterPattern = function(box, length)
33734 var pattern = box.slice(0, length);
33738 Roo.each(pattern, function(i){
33739 format.push(i.size);
33742 Roo.each(standard, function(s){
33744 if(String(s) != String(format)){
33753 if(!match && length == 1){
33758 filterPattern(box, length - 1);
33762 queue.push(pattern);
33764 box = box.slice(length, box.length);
33766 filterPattern(box, 4);
33772 Roo.each(boxes, function(box, k){
33778 if(box.length == 1){
33783 filterPattern(box, 4);
33787 this._processVerticalLayoutQueue( queue, isInstant );
33791 // _verticalAlternativeLayoutItems : function( items , isInstant )
33793 // if ( !items || !items.length ) {
33797 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33801 _horizontalLayoutItems : function ( items , isInstant)
33803 if ( !items || !items.length || items.length < 3) {
33809 var eItems = items.slice(0, 3);
33811 items = items.slice(3, items.length);
33814 ['xs', 'xs', 'xs', 'wide'],
33815 ['xs', 'xs', 'wide'],
33816 ['xs', 'xs', 'sm'],
33817 ['xs', 'xs', 'xs'],
33823 ['sm', 'xs', 'xs'],
33827 ['wide', 'xs', 'xs', 'xs'],
33828 ['wide', 'xs', 'xs'],
33841 Roo.each(items, function(item, k){
33843 switch (item.size) {
33854 boxes.push([item]);
33878 var filterPattern = function(box, length)
33886 var pattern = box.slice(0, length);
33890 Roo.each(pattern, function(i){
33891 format.push(i.size);
33894 Roo.each(standard, function(s){
33896 if(String(s) != String(format)){
33905 if(!match && length == 1){
33910 filterPattern(box, length - 1);
33914 queue.push(pattern);
33916 box = box.slice(length, box.length);
33918 filterPattern(box, 4);
33924 Roo.each(boxes, function(box, k){
33930 if(box.length == 1){
33935 filterPattern(box, 4);
33942 var pos = this.el.getBox(true);
33946 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33948 var hit_end = false;
33950 Roo.each(queue, function(box){
33954 Roo.each(box, function(b){
33956 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33966 Roo.each(box, function(b){
33968 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33971 mx = Math.max(mx, b.x);
33975 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33979 Roo.each(box, function(b){
33981 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33995 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33998 /** Sets position of item in DOM
33999 * @param {Element} item
34000 * @param {Number} x - horizontal position
34001 * @param {Number} y - vertical position
34002 * @param {Boolean} isInstant - disables transitions
34004 _processVerticalLayoutQueue : function( queue, isInstant )
34006 var pos = this.el.getBox(true);
34011 for (var i = 0; i < this.cols; i++){
34015 Roo.each(queue, function(box, k){
34017 var col = k % this.cols;
34019 Roo.each(box, function(b,kk){
34021 b.el.position('absolute');
34023 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34024 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34026 if(b.size == 'md-left' || b.size == 'md-right'){
34027 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34028 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34031 b.el.setWidth(width);
34032 b.el.setHeight(height);
34034 b.el.select('iframe',true).setSize(width,height);
34038 for (var i = 0; i < this.cols; i++){
34040 if(maxY[i] < maxY[col]){
34045 col = Math.min(col, i);
34049 x = pos.x + col * (this.colWidth + this.padWidth);
34053 var positions = [];
34055 switch (box.length){
34057 positions = this.getVerticalOneBoxColPositions(x, y, box);
34060 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34063 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34066 positions = this.getVerticalFourBoxColPositions(x, y, box);
34072 Roo.each(box, function(b,kk){
34074 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34076 var sz = b.el.getSize();
34078 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34086 for (var i = 0; i < this.cols; i++){
34087 mY = Math.max(mY, maxY[i]);
34090 this.el.setHeight(mY - pos.y);
34094 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34096 // var pos = this.el.getBox(true);
34099 // var maxX = pos.right;
34101 // var maxHeight = 0;
34103 // Roo.each(items, function(item, k){
34107 // item.el.position('absolute');
34109 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34111 // item.el.setWidth(width);
34113 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34115 // item.el.setHeight(height);
34118 // item.el.setXY([x, y], isInstant ? false : true);
34120 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34123 // y = y + height + this.alternativePadWidth;
34125 // maxHeight = maxHeight + height + this.alternativePadWidth;
34129 // this.el.setHeight(maxHeight);
34133 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34135 var pos = this.el.getBox(true);
34140 var maxX = pos.right;
34142 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34144 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34146 Roo.each(queue, function(box, k){
34148 Roo.each(box, function(b, kk){
34150 b.el.position('absolute');
34152 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34153 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34155 if(b.size == 'md-left' || b.size == 'md-right'){
34156 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34157 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34160 b.el.setWidth(width);
34161 b.el.setHeight(height);
34169 var positions = [];
34171 switch (box.length){
34173 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34176 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34179 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34182 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34188 Roo.each(box, function(b,kk){
34190 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34192 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34200 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34202 Roo.each(eItems, function(b,k){
34204 b.size = (k == 0) ? 'sm' : 'xs';
34205 b.x = (k == 0) ? 2 : 1;
34206 b.y = (k == 0) ? 2 : 1;
34208 b.el.position('absolute');
34210 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34212 b.el.setWidth(width);
34214 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34216 b.el.setHeight(height);
34220 var positions = [];
34223 x : maxX - this.unitWidth * 2 - this.gutter,
34228 x : maxX - this.unitWidth,
34229 y : minY + (this.unitWidth + this.gutter) * 2
34233 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34237 Roo.each(eItems, function(b,k){
34239 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34245 getVerticalOneBoxColPositions : function(x, y, box)
34249 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34251 if(box[0].size == 'md-left'){
34255 if(box[0].size == 'md-right'){
34260 x : x + (this.unitWidth + this.gutter) * rand,
34267 getVerticalTwoBoxColPositions : function(x, y, box)
34271 if(box[0].size == 'xs'){
34275 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34279 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34293 x : x + (this.unitWidth + this.gutter) * 2,
34294 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34301 getVerticalThreeBoxColPositions : function(x, y, box)
34305 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34313 x : x + (this.unitWidth + this.gutter) * 1,
34318 x : x + (this.unitWidth + this.gutter) * 2,
34326 if(box[0].size == 'xs' && box[1].size == 'xs'){
34335 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34339 x : x + (this.unitWidth + this.gutter) * 1,
34353 x : x + (this.unitWidth + this.gutter) * 2,
34358 x : x + (this.unitWidth + this.gutter) * 2,
34359 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34366 getVerticalFourBoxColPositions : function(x, y, box)
34370 if(box[0].size == 'xs'){
34379 y : y + (this.unitHeight + this.gutter) * 1
34384 y : y + (this.unitHeight + this.gutter) * 2
34388 x : x + (this.unitWidth + this.gutter) * 1,
34402 x : x + (this.unitWidth + this.gutter) * 2,
34407 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34408 y : y + (this.unitHeight + this.gutter) * 1
34412 x : x + (this.unitWidth + this.gutter) * 2,
34413 y : y + (this.unitWidth + this.gutter) * 2
34420 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34424 if(box[0].size == 'md-left'){
34426 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34433 if(box[0].size == 'md-right'){
34435 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34436 y : minY + (this.unitWidth + this.gutter) * 1
34442 var rand = Math.floor(Math.random() * (4 - box[0].y));
34445 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34446 y : minY + (this.unitWidth + this.gutter) * rand
34453 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34457 if(box[0].size == 'xs'){
34460 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34465 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34466 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34474 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34479 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34480 y : minY + (this.unitWidth + this.gutter) * 2
34487 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34491 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34494 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34499 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34500 y : minY + (this.unitWidth + this.gutter) * 1
34504 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34505 y : minY + (this.unitWidth + this.gutter) * 2
34512 if(box[0].size == 'xs' && box[1].size == 'xs'){
34515 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34520 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34525 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34526 y : minY + (this.unitWidth + this.gutter) * 1
34534 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34539 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34540 y : minY + (this.unitWidth + this.gutter) * 2
34544 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34545 y : minY + (this.unitWidth + this.gutter) * 2
34552 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34556 if(box[0].size == 'xs'){
34559 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34564 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34569 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),
34574 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34575 y : minY + (this.unitWidth + this.gutter) * 1
34583 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34588 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34589 y : minY + (this.unitWidth + this.gutter) * 2
34593 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34594 y : minY + (this.unitWidth + this.gutter) * 2
34598 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),
34599 y : minY + (this.unitWidth + this.gutter) * 2
34607 * remove a Masonry Brick
34608 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34610 removeBrick : function(brick_id)
34616 for (var i = 0; i<this.bricks.length; i++) {
34617 if (this.bricks[i].id == brick_id) {
34618 this.bricks.splice(i,1);
34619 this.el.dom.removeChild(Roo.get(brick_id).dom);
34626 * adds a Masonry Brick
34627 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34629 addBrick : function(cfg)
34631 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34632 //this.register(cn);
34633 cn.parentId = this.id;
34634 cn.render(this.el);
34639 * register a Masonry Brick
34640 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34643 register : function(brick)
34645 this.bricks.push(brick);
34646 brick.masonryId = this.id;
34650 * clear all the Masonry Brick
34652 clearAll : function()
34655 //this.getChildContainer().dom.innerHTML = "";
34656 this.el.dom.innerHTML = '';
34659 getSelected : function()
34661 if (!this.selectedBrick) {
34665 return this.selectedBrick;
34669 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34673 * register a Masonry Layout
34674 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34677 register : function(layout)
34679 this.groups[layout.id] = layout;
34682 * fetch a Masonry Layout based on the masonry layout ID
34683 * @param {string} the masonry layout to add
34684 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34687 get: function(layout_id) {
34688 if (typeof(this.groups[layout_id]) == 'undefined') {
34691 return this.groups[layout_id] ;
34703 * http://masonry.desandro.com
34705 * The idea is to render all the bricks based on vertical width...
34707 * The original code extends 'outlayer' - we might need to use that....
34713 * @class Roo.bootstrap.LayoutMasonryAuto
34714 * @extends Roo.bootstrap.Component
34715 * Bootstrap Layout Masonry class
34718 * Create a new Element
34719 * @param {Object} config The config object
34722 Roo.bootstrap.LayoutMasonryAuto = function(config){
34723 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34726 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34729 * @cfg {Boolean} isFitWidth - resize the width..
34731 isFitWidth : false, // options..
34733 * @cfg {Boolean} isOriginLeft = left align?
34735 isOriginLeft : true,
34737 * @cfg {Boolean} isOriginTop = top align?
34739 isOriginTop : false,
34741 * @cfg {Boolean} isLayoutInstant = no animation?
34743 isLayoutInstant : false, // needed?
34745 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34747 isResizingContainer : true,
34749 * @cfg {Number} columnWidth width of the columns
34755 * @cfg {Number} maxCols maximum number of columns
34760 * @cfg {Number} padHeight padding below box..
34766 * @cfg {Boolean} isAutoInitial defalut true
34769 isAutoInitial : true,
34775 initialColumnWidth : 0,
34776 currentSize : null,
34778 colYs : null, // array.
34785 bricks: null, //CompositeElement
34786 cols : 0, // array?
34787 // element : null, // wrapped now this.el
34788 _isLayoutInited : null,
34791 getAutoCreate : function(){
34795 cls: 'blog-masonary-wrapper ' + this.cls,
34797 cls : 'mas-boxes masonary'
34804 getChildContainer: function( )
34806 if (this.boxesEl) {
34807 return this.boxesEl;
34810 this.boxesEl = this.el.select('.mas-boxes').first();
34812 return this.boxesEl;
34816 initEvents : function()
34820 if(this.isAutoInitial){
34821 Roo.log('hook children rendered');
34822 this.on('childrenrendered', function() {
34823 Roo.log('children rendered');
34830 initial : function()
34832 this.reloadItems();
34834 this.currentSize = this.el.getBox(true);
34836 /// was window resize... - let's see if this works..
34837 Roo.EventManager.onWindowResize(this.resize, this);
34839 if(!this.isAutoInitial){
34844 this.layout.defer(500,this);
34847 reloadItems: function()
34849 this.bricks = this.el.select('.masonry-brick', true);
34851 this.bricks.each(function(b) {
34852 //Roo.log(b.getSize());
34853 if (!b.attr('originalwidth')) {
34854 b.attr('originalwidth', b.getSize().width);
34859 Roo.log(this.bricks.elements.length);
34862 resize : function()
34865 var cs = this.el.getBox(true);
34867 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34868 Roo.log("no change in with or X");
34871 this.currentSize = cs;
34875 layout : function()
34878 this._resetLayout();
34879 //this._manageStamps();
34881 // don't animate first layout
34882 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34883 this.layoutItems( isInstant );
34885 // flag for initalized
34886 this._isLayoutInited = true;
34889 layoutItems : function( isInstant )
34891 //var items = this._getItemsForLayout( this.items );
34892 // original code supports filtering layout items.. we just ignore it..
34894 this._layoutItems( this.bricks , isInstant );
34896 this._postLayout();
34898 _layoutItems : function ( items , isInstant)
34900 //this.fireEvent( 'layout', this, items );
34903 if ( !items || !items.elements.length ) {
34904 // no items, emit event with empty array
34909 items.each(function(item) {
34910 Roo.log("layout item");
34912 // get x/y object from method
34913 var position = this._getItemLayoutPosition( item );
34915 position.item = item;
34916 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34917 queue.push( position );
34920 this._processLayoutQueue( queue );
34922 /** Sets position of item in DOM
34923 * @param {Element} item
34924 * @param {Number} x - horizontal position
34925 * @param {Number} y - vertical position
34926 * @param {Boolean} isInstant - disables transitions
34928 _processLayoutQueue : function( queue )
34930 for ( var i=0, len = queue.length; i < len; i++ ) {
34931 var obj = queue[i];
34932 obj.item.position('absolute');
34933 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34939 * Any logic you want to do after each layout,
34940 * i.e. size the container
34942 _postLayout : function()
34944 this.resizeContainer();
34947 resizeContainer : function()
34949 if ( !this.isResizingContainer ) {
34952 var size = this._getContainerSize();
34954 this.el.setSize(size.width,size.height);
34955 this.boxesEl.setSize(size.width,size.height);
34961 _resetLayout : function()
34963 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34964 this.colWidth = this.el.getWidth();
34965 //this.gutter = this.el.getWidth();
34967 this.measureColumns();
34973 this.colYs.push( 0 );
34979 measureColumns : function()
34981 this.getContainerWidth();
34982 // if columnWidth is 0, default to outerWidth of first item
34983 if ( !this.columnWidth ) {
34984 var firstItem = this.bricks.first();
34985 Roo.log(firstItem);
34986 this.columnWidth = this.containerWidth;
34987 if (firstItem && firstItem.attr('originalwidth') ) {
34988 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34990 // columnWidth fall back to item of first element
34991 Roo.log("set column width?");
34992 this.initialColumnWidth = this.columnWidth ;
34994 // if first elem has no width, default to size of container
34999 if (this.initialColumnWidth) {
35000 this.columnWidth = this.initialColumnWidth;
35005 // column width is fixed at the top - however if container width get's smaller we should
35008 // this bit calcs how man columns..
35010 var columnWidth = this.columnWidth += this.gutter;
35012 // calculate columns
35013 var containerWidth = this.containerWidth + this.gutter;
35015 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35016 // fix rounding errors, typically with gutters
35017 var excess = columnWidth - containerWidth % columnWidth;
35020 // if overshoot is less than a pixel, round up, otherwise floor it
35021 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35022 cols = Math[ mathMethod ]( cols );
35023 this.cols = Math.max( cols, 1 );
35024 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35026 // padding positioning..
35027 var totalColWidth = this.cols * this.columnWidth;
35028 var padavail = this.containerWidth - totalColWidth;
35029 // so for 2 columns - we need 3 'pads'
35031 var padNeeded = (1+this.cols) * this.padWidth;
35033 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35035 this.columnWidth += padExtra
35036 //this.padWidth = Math.floor(padavail / ( this.cols));
35038 // adjust colum width so that padding is fixed??
35040 // we have 3 columns ... total = width * 3
35041 // we have X left over... that should be used by
35043 //if (this.expandC) {
35051 getContainerWidth : function()
35053 /* // container is parent if fit width
35054 var container = this.isFitWidth ? this.element.parentNode : this.element;
35055 // check that this.size and size are there
35056 // IE8 triggers resize on body size change, so they might not be
35058 var size = getSize( container ); //FIXME
35059 this.containerWidth = size && size.innerWidth; //FIXME
35062 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35066 _getItemLayoutPosition : function( item ) // what is item?
35068 // we resize the item to our columnWidth..
35070 item.setWidth(this.columnWidth);
35071 item.autoBoxAdjust = false;
35073 var sz = item.getSize();
35075 // how many columns does this brick span
35076 var remainder = this.containerWidth % this.columnWidth;
35078 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35079 // round if off by 1 pixel, otherwise use ceil
35080 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35081 colSpan = Math.min( colSpan, this.cols );
35083 // normally this should be '1' as we dont' currently allow multi width columns..
35085 var colGroup = this._getColGroup( colSpan );
35086 // get the minimum Y value from the columns
35087 var minimumY = Math.min.apply( Math, colGroup );
35088 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35090 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35092 // position the brick
35094 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35095 y: this.currentSize.y + minimumY + this.padHeight
35099 // apply setHeight to necessary columns
35100 var setHeight = minimumY + sz.height + this.padHeight;
35101 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35103 var setSpan = this.cols + 1 - colGroup.length;
35104 for ( var i = 0; i < setSpan; i++ ) {
35105 this.colYs[ shortColIndex + i ] = setHeight ;
35112 * @param {Number} colSpan - number of columns the element spans
35113 * @returns {Array} colGroup
35115 _getColGroup : function( colSpan )
35117 if ( colSpan < 2 ) {
35118 // if brick spans only one column, use all the column Ys
35123 // how many different places could this brick fit horizontally
35124 var groupCount = this.cols + 1 - colSpan;
35125 // for each group potential horizontal position
35126 for ( var i = 0; i < groupCount; i++ ) {
35127 // make an array of colY values for that one group
35128 var groupColYs = this.colYs.slice( i, i + colSpan );
35129 // and get the max value of the array
35130 colGroup[i] = Math.max.apply( Math, groupColYs );
35135 _manageStamp : function( stamp )
35137 var stampSize = stamp.getSize();
35138 var offset = stamp.getBox();
35139 // get the columns that this stamp affects
35140 var firstX = this.isOriginLeft ? offset.x : offset.right;
35141 var lastX = firstX + stampSize.width;
35142 var firstCol = Math.floor( firstX / this.columnWidth );
35143 firstCol = Math.max( 0, firstCol );
35145 var lastCol = Math.floor( lastX / this.columnWidth );
35146 // lastCol should not go over if multiple of columnWidth #425
35147 lastCol -= lastX % this.columnWidth ? 0 : 1;
35148 lastCol = Math.min( this.cols - 1, lastCol );
35150 // set colYs to bottom of the stamp
35151 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35154 for ( var i = firstCol; i <= lastCol; i++ ) {
35155 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35160 _getContainerSize : function()
35162 this.maxY = Math.max.apply( Math, this.colYs );
35167 if ( this.isFitWidth ) {
35168 size.width = this._getContainerFitWidth();
35174 _getContainerFitWidth : function()
35176 var unusedCols = 0;
35177 // count unused columns
35180 if ( this.colYs[i] !== 0 ) {
35185 // fit container to columns that have been used
35186 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35189 needsResizeLayout : function()
35191 var previousWidth = this.containerWidth;
35192 this.getContainerWidth();
35193 return previousWidth !== this.containerWidth;
35208 * @class Roo.bootstrap.MasonryBrick
35209 * @extends Roo.bootstrap.Component
35210 * Bootstrap MasonryBrick class
35213 * Create a new MasonryBrick
35214 * @param {Object} config The config object
35217 Roo.bootstrap.MasonryBrick = function(config){
35219 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35221 Roo.bootstrap.MasonryBrick.register(this);
35227 * When a MasonryBrick is clcik
35228 * @param {Roo.bootstrap.MasonryBrick} this
35229 * @param {Roo.EventObject} e
35235 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35238 * @cfg {String} title
35242 * @cfg {String} html
35246 * @cfg {String} bgimage
35250 * @cfg {String} videourl
35254 * @cfg {String} cls
35258 * @cfg {String} href
35262 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35267 * @cfg {String} placetitle (center|bottom)
35272 * @cfg {Boolean} isFitContainer defalut true
35274 isFitContainer : true,
35277 * @cfg {Boolean} preventDefault defalut false
35279 preventDefault : false,
35282 * @cfg {Boolean} inverse defalut false
35284 maskInverse : false,
35286 getAutoCreate : function()
35288 if(!this.isFitContainer){
35289 return this.getSplitAutoCreate();
35292 var cls = 'masonry-brick masonry-brick-full';
35294 if(this.href.length){
35295 cls += ' masonry-brick-link';
35298 if(this.bgimage.length){
35299 cls += ' masonry-brick-image';
35302 if(this.maskInverse){
35303 cls += ' mask-inverse';
35306 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35307 cls += ' enable-mask';
35311 cls += ' masonry-' + this.size + '-brick';
35314 if(this.placetitle.length){
35316 switch (this.placetitle) {
35318 cls += ' masonry-center-title';
35321 cls += ' masonry-bottom-title';
35328 if(!this.html.length && !this.bgimage.length){
35329 cls += ' masonry-center-title';
35332 if(!this.html.length && this.bgimage.length){
35333 cls += ' masonry-bottom-title';
35338 cls += ' ' + this.cls;
35342 tag: (this.href.length) ? 'a' : 'div',
35347 cls: 'masonry-brick-mask'
35351 cls: 'masonry-brick-paragraph',
35357 if(this.href.length){
35358 cfg.href = this.href;
35361 var cn = cfg.cn[1].cn;
35363 if(this.title.length){
35366 cls: 'masonry-brick-title',
35371 if(this.html.length){
35374 cls: 'masonry-brick-text',
35379 if (!this.title.length && !this.html.length) {
35380 cfg.cn[1].cls += ' hide';
35383 if(this.bgimage.length){
35386 cls: 'masonry-brick-image-view',
35391 if(this.videourl.length){
35392 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35393 // youtube support only?
35396 cls: 'masonry-brick-image-view',
35399 allowfullscreen : true
35407 getSplitAutoCreate : function()
35409 var cls = 'masonry-brick masonry-brick-split';
35411 if(this.href.length){
35412 cls += ' masonry-brick-link';
35415 if(this.bgimage.length){
35416 cls += ' masonry-brick-image';
35420 cls += ' masonry-' + this.size + '-brick';
35423 switch (this.placetitle) {
35425 cls += ' masonry-center-title';
35428 cls += ' masonry-bottom-title';
35431 if(!this.bgimage.length){
35432 cls += ' masonry-center-title';
35435 if(this.bgimage.length){
35436 cls += ' masonry-bottom-title';
35442 cls += ' ' + this.cls;
35446 tag: (this.href.length) ? 'a' : 'div',
35451 cls: 'masonry-brick-split-head',
35455 cls: 'masonry-brick-paragraph',
35462 cls: 'masonry-brick-split-body',
35468 if(this.href.length){
35469 cfg.href = this.href;
35472 if(this.title.length){
35473 cfg.cn[0].cn[0].cn.push({
35475 cls: 'masonry-brick-title',
35480 if(this.html.length){
35481 cfg.cn[1].cn.push({
35483 cls: 'masonry-brick-text',
35488 if(this.bgimage.length){
35489 cfg.cn[0].cn.push({
35491 cls: 'masonry-brick-image-view',
35496 if(this.videourl.length){
35497 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35498 // youtube support only?
35499 cfg.cn[0].cn.cn.push({
35501 cls: 'masonry-brick-image-view',
35504 allowfullscreen : true
35511 initEvents: function()
35513 switch (this.size) {
35546 this.el.on('touchstart', this.onTouchStart, this);
35547 this.el.on('touchmove', this.onTouchMove, this);
35548 this.el.on('touchend', this.onTouchEnd, this);
35549 this.el.on('contextmenu', this.onContextMenu, this);
35551 this.el.on('mouseenter' ,this.enter, this);
35552 this.el.on('mouseleave', this.leave, this);
35553 this.el.on('click', this.onClick, this);
35556 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35557 this.parent().bricks.push(this);
35562 onClick: function(e, el)
35564 var time = this.endTimer - this.startTimer;
35565 // Roo.log(e.preventDefault());
35568 e.preventDefault();
35573 if(!this.preventDefault){
35577 e.preventDefault();
35579 if (this.activeClass != '') {
35580 this.selectBrick();
35583 this.fireEvent('click', this, e);
35586 enter: function(e, el)
35588 e.preventDefault();
35590 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35594 if(this.bgimage.length && this.html.length){
35595 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35599 leave: function(e, el)
35601 e.preventDefault();
35603 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35607 if(this.bgimage.length && this.html.length){
35608 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35612 onTouchStart: function(e, el)
35614 // e.preventDefault();
35616 this.touchmoved = false;
35618 if(!this.isFitContainer){
35622 if(!this.bgimage.length || !this.html.length){
35626 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35628 this.timer = new Date().getTime();
35632 onTouchMove: function(e, el)
35634 this.touchmoved = true;
35637 onContextMenu : function(e,el)
35639 e.preventDefault();
35640 e.stopPropagation();
35644 onTouchEnd: function(e, el)
35646 // e.preventDefault();
35648 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35655 if(!this.bgimage.length || !this.html.length){
35657 if(this.href.length){
35658 window.location.href = this.href;
35664 if(!this.isFitContainer){
35668 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35670 window.location.href = this.href;
35673 //selection on single brick only
35674 selectBrick : function() {
35676 if (!this.parentId) {
35680 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35681 var index = m.selectedBrick.indexOf(this.id);
35684 m.selectedBrick.splice(index,1);
35685 this.el.removeClass(this.activeClass);
35689 for(var i = 0; i < m.selectedBrick.length; i++) {
35690 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35691 b.el.removeClass(b.activeClass);
35694 m.selectedBrick = [];
35696 m.selectedBrick.push(this.id);
35697 this.el.addClass(this.activeClass);
35701 isSelected : function(){
35702 return this.el.hasClass(this.activeClass);
35707 Roo.apply(Roo.bootstrap.MasonryBrick, {
35710 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35712 * register a Masonry Brick
35713 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35716 register : function(brick)
35718 //this.groups[brick.id] = brick;
35719 this.groups.add(brick.id, brick);
35722 * fetch a masonry brick based on the masonry brick ID
35723 * @param {string} the masonry brick to add
35724 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35727 get: function(brick_id)
35729 // if (typeof(this.groups[brick_id]) == 'undefined') {
35732 // return this.groups[brick_id] ;
35734 if(this.groups.key(brick_id)) {
35735 return this.groups.key(brick_id);
35753 * @class Roo.bootstrap.Brick
35754 * @extends Roo.bootstrap.Component
35755 * Bootstrap Brick class
35758 * Create a new Brick
35759 * @param {Object} config The config object
35762 Roo.bootstrap.Brick = function(config){
35763 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35769 * When a Brick is click
35770 * @param {Roo.bootstrap.Brick} this
35771 * @param {Roo.EventObject} e
35777 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35780 * @cfg {String} title
35784 * @cfg {String} html
35788 * @cfg {String} bgimage
35792 * @cfg {String} cls
35796 * @cfg {String} href
35800 * @cfg {String} video
35804 * @cfg {Boolean} square
35808 getAutoCreate : function()
35810 var cls = 'roo-brick';
35812 if(this.href.length){
35813 cls += ' roo-brick-link';
35816 if(this.bgimage.length){
35817 cls += ' roo-brick-image';
35820 if(!this.html.length && !this.bgimage.length){
35821 cls += ' roo-brick-center-title';
35824 if(!this.html.length && this.bgimage.length){
35825 cls += ' roo-brick-bottom-title';
35829 cls += ' ' + this.cls;
35833 tag: (this.href.length) ? 'a' : 'div',
35838 cls: 'roo-brick-paragraph',
35844 if(this.href.length){
35845 cfg.href = this.href;
35848 var cn = cfg.cn[0].cn;
35850 if(this.title.length){
35853 cls: 'roo-brick-title',
35858 if(this.html.length){
35861 cls: 'roo-brick-text',
35868 if(this.bgimage.length){
35871 cls: 'roo-brick-image-view',
35879 initEvents: function()
35881 if(this.title.length || this.html.length){
35882 this.el.on('mouseenter' ,this.enter, this);
35883 this.el.on('mouseleave', this.leave, this);
35886 Roo.EventManager.onWindowResize(this.resize, this);
35888 if(this.bgimage.length){
35889 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35890 this.imageEl.on('load', this.onImageLoad, this);
35897 onImageLoad : function()
35902 resize : function()
35904 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35906 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35908 if(this.bgimage.length){
35909 var image = this.el.select('.roo-brick-image-view', true).first();
35911 image.setWidth(paragraph.getWidth());
35914 image.setHeight(paragraph.getWidth());
35917 this.el.setHeight(image.getHeight());
35918 paragraph.setHeight(image.getHeight());
35924 enter: function(e, el)
35926 e.preventDefault();
35928 if(this.bgimage.length){
35929 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35930 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35934 leave: function(e, el)
35936 e.preventDefault();
35938 if(this.bgimage.length){
35939 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35940 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35955 * @class Roo.bootstrap.NumberField
35956 * @extends Roo.bootstrap.Input
35957 * Bootstrap NumberField class
35963 * Create a new NumberField
35964 * @param {Object} config The config object
35967 Roo.bootstrap.NumberField = function(config){
35968 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35971 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35974 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35976 allowDecimals : true,
35978 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35980 decimalSeparator : ".",
35982 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35984 decimalPrecision : 2,
35986 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35988 allowNegative : true,
35991 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35995 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35997 minValue : Number.NEGATIVE_INFINITY,
35999 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36001 maxValue : Number.MAX_VALUE,
36003 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36005 minText : "The minimum value for this field is {0}",
36007 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36009 maxText : "The maximum value for this field is {0}",
36011 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36012 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36014 nanText : "{0} is not a valid number",
36016 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36018 thousandsDelimiter : false,
36020 * @cfg {String} valueAlign alignment of value
36022 valueAlign : "left",
36024 getAutoCreate : function()
36026 var hiddenInput = {
36030 cls: 'hidden-number-input'
36034 hiddenInput.name = this.name;
36039 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36041 this.name = hiddenInput.name;
36043 if(cfg.cn.length > 0) {
36044 cfg.cn.push(hiddenInput);
36051 initEvents : function()
36053 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36055 var allowed = "0123456789";
36057 if(this.allowDecimals){
36058 allowed += this.decimalSeparator;
36061 if(this.allowNegative){
36065 if(this.thousandsDelimiter) {
36069 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36071 var keyPress = function(e){
36073 var k = e.getKey();
36075 var c = e.getCharCode();
36078 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36079 allowed.indexOf(String.fromCharCode(c)) === -1
36085 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36089 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36094 this.el.on("keypress", keyPress, this);
36097 validateValue : function(value)
36100 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36104 var num = this.parseValue(value);
36107 this.markInvalid(String.format(this.nanText, value));
36111 if(num < this.minValue){
36112 this.markInvalid(String.format(this.minText, this.minValue));
36116 if(num > this.maxValue){
36117 this.markInvalid(String.format(this.maxText, this.maxValue));
36124 getValue : function()
36126 var v = this.hiddenEl().getValue();
36128 return this.fixPrecision(this.parseValue(v));
36131 parseValue : function(value)
36133 if(this.thousandsDelimiter) {
36135 r = new RegExp(",", "g");
36136 value = value.replace(r, "");
36139 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36140 return isNaN(value) ? '' : value;
36143 fixPrecision : function(value)
36145 if(this.thousandsDelimiter) {
36147 r = new RegExp(",", "g");
36148 value = value.replace(r, "");
36151 var nan = isNaN(value);
36153 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36154 return nan ? '' : value;
36156 return parseFloat(value).toFixed(this.decimalPrecision);
36159 setValue : function(v)
36161 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36167 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36169 this.inputEl().dom.value = (v == '') ? '' :
36170 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36172 if(!this.allowZero && v === '0') {
36173 this.hiddenEl().dom.value = '';
36174 this.inputEl().dom.value = '';
36181 decimalPrecisionFcn : function(v)
36183 return Math.floor(v);
36186 beforeBlur : function()
36188 var v = this.parseValue(this.getRawValue());
36190 if(v || v === 0 || v === ''){
36195 hiddenEl : function()
36197 return this.el.select('input.hidden-number-input',true).first();
36209 * @class Roo.bootstrap.DocumentSlider
36210 * @extends Roo.bootstrap.Component
36211 * Bootstrap DocumentSlider class
36214 * Create a new DocumentViewer
36215 * @param {Object} config The config object
36218 Roo.bootstrap.DocumentSlider = function(config){
36219 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36226 * Fire after initEvent
36227 * @param {Roo.bootstrap.DocumentSlider} this
36232 * Fire after update
36233 * @param {Roo.bootstrap.DocumentSlider} this
36239 * @param {Roo.bootstrap.DocumentSlider} this
36245 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36251 getAutoCreate : function()
36255 cls : 'roo-document-slider',
36259 cls : 'roo-document-slider-header',
36263 cls : 'roo-document-slider-header-title'
36269 cls : 'roo-document-slider-body',
36273 cls : 'roo-document-slider-prev',
36277 cls : 'fa fa-chevron-left'
36283 cls : 'roo-document-slider-thumb',
36287 cls : 'roo-document-slider-image'
36293 cls : 'roo-document-slider-next',
36297 cls : 'fa fa-chevron-right'
36309 initEvents : function()
36311 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36312 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36314 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36315 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36317 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36318 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36320 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36321 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36323 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36324 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36326 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36327 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36329 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36330 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36332 this.thumbEl.on('click', this.onClick, this);
36334 this.prevIndicator.on('click', this.prev, this);
36336 this.nextIndicator.on('click', this.next, this);
36340 initial : function()
36342 if(this.files.length){
36343 this.indicator = 1;
36347 this.fireEvent('initial', this);
36350 update : function()
36352 this.imageEl.attr('src', this.files[this.indicator - 1]);
36354 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36356 this.prevIndicator.show();
36358 if(this.indicator == 1){
36359 this.prevIndicator.hide();
36362 this.nextIndicator.show();
36364 if(this.indicator == this.files.length){
36365 this.nextIndicator.hide();
36368 this.thumbEl.scrollTo('top');
36370 this.fireEvent('update', this);
36373 onClick : function(e)
36375 e.preventDefault();
36377 this.fireEvent('click', this);
36382 e.preventDefault();
36384 this.indicator = Math.max(1, this.indicator - 1);
36391 e.preventDefault();
36393 this.indicator = Math.min(this.files.length, this.indicator + 1);
36407 * @class Roo.bootstrap.RadioSet
36408 * @extends Roo.bootstrap.Input
36409 * Bootstrap RadioSet class
36410 * @cfg {String} indicatorpos (left|right) default left
36411 * @cfg {Boolean} inline (true|false) inline the element (default true)
36412 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36414 * Create a new RadioSet
36415 * @param {Object} config The config object
36418 Roo.bootstrap.RadioSet = function(config){
36420 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36424 Roo.bootstrap.RadioSet.register(this);
36429 * Fires when the element is checked or unchecked.
36430 * @param {Roo.bootstrap.RadioSet} this This radio
36431 * @param {Roo.bootstrap.Radio} item The checked item
36436 * Fires when the element is click.
36437 * @param {Roo.bootstrap.RadioSet} this This radio set
36438 * @param {Roo.bootstrap.Radio} item The checked item
36439 * @param {Roo.EventObject} e The event object
36446 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36454 indicatorpos : 'left',
36456 getAutoCreate : function()
36460 cls : 'roo-radio-set-label',
36464 html : this.fieldLabel
36468 if (Roo.bootstrap.version == 3) {
36471 if(this.indicatorpos == 'left'){
36474 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36475 tooltip : 'This field is required'
36480 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36481 tooltip : 'This field is required'
36487 cls : 'roo-radio-set-items'
36490 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36492 if (align === 'left' && this.fieldLabel.length) {
36495 cls : "roo-radio-set-right",
36501 if(this.labelWidth > 12){
36502 label.style = "width: " + this.labelWidth + 'px';
36505 if(this.labelWidth < 13 && this.labelmd == 0){
36506 this.labelmd = this.labelWidth;
36509 if(this.labellg > 0){
36510 label.cls += ' col-lg-' + this.labellg;
36511 items.cls += ' col-lg-' + (12 - this.labellg);
36514 if(this.labelmd > 0){
36515 label.cls += ' col-md-' + this.labelmd;
36516 items.cls += ' col-md-' + (12 - this.labelmd);
36519 if(this.labelsm > 0){
36520 label.cls += ' col-sm-' + this.labelsm;
36521 items.cls += ' col-sm-' + (12 - this.labelsm);
36524 if(this.labelxs > 0){
36525 label.cls += ' col-xs-' + this.labelxs;
36526 items.cls += ' col-xs-' + (12 - this.labelxs);
36532 cls : 'roo-radio-set',
36536 cls : 'roo-radio-set-input',
36539 value : this.value ? this.value : ''
36546 if(this.weight.length){
36547 cfg.cls += ' roo-radio-' + this.weight;
36551 cfg.cls += ' roo-radio-set-inline';
36555 ['xs','sm','md','lg'].map(function(size){
36556 if (settings[size]) {
36557 cfg.cls += ' col-' + size + '-' + settings[size];
36565 initEvents : function()
36567 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36568 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36570 if(!this.fieldLabel.length){
36571 this.labelEl.hide();
36574 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36575 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36577 this.indicator = this.indicatorEl();
36579 if(this.indicator){
36580 this.indicator.addClass('invisible');
36583 this.originalValue = this.getValue();
36587 inputEl: function ()
36589 return this.el.select('.roo-radio-set-input', true).first();
36592 getChildContainer : function()
36594 return this.itemsEl;
36597 register : function(item)
36599 this.radioes.push(item);
36603 validate : function()
36605 if(this.getVisibilityEl().hasClass('hidden')){
36611 Roo.each(this.radioes, function(i){
36620 if(this.allowBlank) {
36624 if(this.disabled || valid){
36629 this.markInvalid();
36634 markValid : function()
36636 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36637 this.indicatorEl().removeClass('visible');
36638 this.indicatorEl().addClass('invisible');
36642 if (Roo.bootstrap.version == 3) {
36643 this.el.removeClass([this.invalidClass, this.validClass]);
36644 this.el.addClass(this.validClass);
36646 this.el.removeClass(['is-invalid','is-valid']);
36647 this.el.addClass(['is-valid']);
36649 this.fireEvent('valid', this);
36652 markInvalid : function(msg)
36654 if(this.allowBlank || this.disabled){
36658 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36659 this.indicatorEl().removeClass('invisible');
36660 this.indicatorEl().addClass('visible');
36662 if (Roo.bootstrap.version == 3) {
36663 this.el.removeClass([this.invalidClass, this.validClass]);
36664 this.el.addClass(this.invalidClass);
36666 this.el.removeClass(['is-invalid','is-valid']);
36667 this.el.addClass(['is-invalid']);
36670 this.fireEvent('invalid', this, msg);
36674 setValue : function(v, suppressEvent)
36676 if(this.value === v){
36683 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36686 Roo.each(this.radioes, function(i){
36688 i.el.removeClass('checked');
36691 Roo.each(this.radioes, function(i){
36693 if(i.value === v || i.value.toString() === v.toString()){
36695 i.el.addClass('checked');
36697 if(suppressEvent !== true){
36698 this.fireEvent('check', this, i);
36709 clearInvalid : function(){
36711 if(!this.el || this.preventMark){
36715 this.el.removeClass([this.invalidClass]);
36717 this.fireEvent('valid', this);
36722 Roo.apply(Roo.bootstrap.RadioSet, {
36726 register : function(set)
36728 this.groups[set.name] = set;
36731 get: function(name)
36733 if (typeof(this.groups[name]) == 'undefined') {
36737 return this.groups[name] ;
36743 * Ext JS Library 1.1.1
36744 * Copyright(c) 2006-2007, Ext JS, LLC.
36746 * Originally Released Under LGPL - original licence link has changed is not relivant.
36749 * <script type="text/javascript">
36754 * @class Roo.bootstrap.SplitBar
36755 * @extends Roo.util.Observable
36756 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36760 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36761 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36762 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36763 split.minSize = 100;
36764 split.maxSize = 600;
36765 split.animate = true;
36766 split.on('moved', splitterMoved);
36769 * Create a new SplitBar
36770 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36771 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36772 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36773 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36774 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36775 position of the SplitBar).
36777 Roo.bootstrap.SplitBar = function(cfg){
36782 // dragElement : elm
36783 // resizingElement: el,
36785 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36786 // placement : Roo.bootstrap.SplitBar.LEFT ,
36787 // existingProxy ???
36790 this.el = Roo.get(cfg.dragElement, true);
36791 this.el.dom.unselectable = "on";
36793 this.resizingEl = Roo.get(cfg.resizingElement, true);
36797 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36798 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36801 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36804 * The minimum size of the resizing element. (Defaults to 0)
36810 * The maximum size of the resizing element. (Defaults to 2000)
36813 this.maxSize = 2000;
36816 * Whether to animate the transition to the new size
36819 this.animate = false;
36822 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36825 this.useShim = false;
36830 if(!cfg.existingProxy){
36832 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36834 this.proxy = Roo.get(cfg.existingProxy).dom;
36837 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36840 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36843 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36846 this.dragSpecs = {};
36849 * @private The adapter to use to positon and resize elements
36851 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36852 this.adapter.init(this);
36854 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36856 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36857 this.el.addClass("roo-splitbar-h");
36860 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36861 this.el.addClass("roo-splitbar-v");
36867 * Fires when the splitter is moved (alias for {@link #event-moved})
36868 * @param {Roo.bootstrap.SplitBar} this
36869 * @param {Number} newSize the new width or height
36874 * Fires when the splitter is moved
36875 * @param {Roo.bootstrap.SplitBar} this
36876 * @param {Number} newSize the new width or height
36880 * @event beforeresize
36881 * Fires before the splitter is dragged
36882 * @param {Roo.bootstrap.SplitBar} this
36884 "beforeresize" : true,
36886 "beforeapply" : true
36889 Roo.util.Observable.call(this);
36892 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36893 onStartProxyDrag : function(x, y){
36894 this.fireEvent("beforeresize", this);
36896 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36898 o.enableDisplayMode("block");
36899 // all splitbars share the same overlay
36900 Roo.bootstrap.SplitBar.prototype.overlay = o;
36902 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36903 this.overlay.show();
36904 Roo.get(this.proxy).setDisplayed("block");
36905 var size = this.adapter.getElementSize(this);
36906 this.activeMinSize = this.getMinimumSize();;
36907 this.activeMaxSize = this.getMaximumSize();;
36908 var c1 = size - this.activeMinSize;
36909 var c2 = Math.max(this.activeMaxSize - size, 0);
36910 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36911 this.dd.resetConstraints();
36912 this.dd.setXConstraint(
36913 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36914 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36916 this.dd.setYConstraint(0, 0);
36918 this.dd.resetConstraints();
36919 this.dd.setXConstraint(0, 0);
36920 this.dd.setYConstraint(
36921 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36922 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36925 this.dragSpecs.startSize = size;
36926 this.dragSpecs.startPoint = [x, y];
36927 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36931 * @private Called after the drag operation by the DDProxy
36933 onEndProxyDrag : function(e){
36934 Roo.get(this.proxy).setDisplayed(false);
36935 var endPoint = Roo.lib.Event.getXY(e);
36937 this.overlay.hide();
36940 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36941 newSize = this.dragSpecs.startSize +
36942 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36943 endPoint[0] - this.dragSpecs.startPoint[0] :
36944 this.dragSpecs.startPoint[0] - endPoint[0]
36947 newSize = this.dragSpecs.startSize +
36948 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36949 endPoint[1] - this.dragSpecs.startPoint[1] :
36950 this.dragSpecs.startPoint[1] - endPoint[1]
36953 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36954 if(newSize != this.dragSpecs.startSize){
36955 if(this.fireEvent('beforeapply', this, newSize) !== false){
36956 this.adapter.setElementSize(this, newSize);
36957 this.fireEvent("moved", this, newSize);
36958 this.fireEvent("resize", this, newSize);
36964 * Get the adapter this SplitBar uses
36965 * @return The adapter object
36967 getAdapter : function(){
36968 return this.adapter;
36972 * Set the adapter this SplitBar uses
36973 * @param {Object} adapter A SplitBar adapter object
36975 setAdapter : function(adapter){
36976 this.adapter = adapter;
36977 this.adapter.init(this);
36981 * Gets the minimum size for the resizing element
36982 * @return {Number} The minimum size
36984 getMinimumSize : function(){
36985 return this.minSize;
36989 * Sets the minimum size for the resizing element
36990 * @param {Number} minSize The minimum size
36992 setMinimumSize : function(minSize){
36993 this.minSize = minSize;
36997 * Gets the maximum size for the resizing element
36998 * @return {Number} The maximum size
37000 getMaximumSize : function(){
37001 return this.maxSize;
37005 * Sets the maximum size for the resizing element
37006 * @param {Number} maxSize The maximum size
37008 setMaximumSize : function(maxSize){
37009 this.maxSize = maxSize;
37013 * Sets the initialize size for the resizing element
37014 * @param {Number} size The initial size
37016 setCurrentSize : function(size){
37017 var oldAnimate = this.animate;
37018 this.animate = false;
37019 this.adapter.setElementSize(this, size);
37020 this.animate = oldAnimate;
37024 * Destroy this splitbar.
37025 * @param {Boolean} removeEl True to remove the element
37027 destroy : function(removeEl){
37029 this.shim.remove();
37032 this.proxy.parentNode.removeChild(this.proxy);
37040 * @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.
37042 Roo.bootstrap.SplitBar.createProxy = function(dir){
37043 var proxy = new Roo.Element(document.createElement("div"));
37044 proxy.unselectable();
37045 var cls = 'roo-splitbar-proxy';
37046 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37047 document.body.appendChild(proxy.dom);
37052 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37053 * Default Adapter. It assumes the splitter and resizing element are not positioned
37054 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37056 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37059 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37060 // do nothing for now
37061 init : function(s){
37065 * Called before drag operations to get the current size of the resizing element.
37066 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37068 getElementSize : function(s){
37069 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37070 return s.resizingEl.getWidth();
37072 return s.resizingEl.getHeight();
37077 * Called after drag operations to set the size of the resizing element.
37078 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37079 * @param {Number} newSize The new size to set
37080 * @param {Function} onComplete A function to be invoked when resizing is complete
37082 setElementSize : function(s, newSize, onComplete){
37083 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37085 s.resizingEl.setWidth(newSize);
37087 onComplete(s, newSize);
37090 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37095 s.resizingEl.setHeight(newSize);
37097 onComplete(s, newSize);
37100 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37107 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37108 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37109 * Adapter that moves the splitter element to align with the resized sizing element.
37110 * Used with an absolute positioned SplitBar.
37111 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37112 * document.body, make sure you assign an id to the body element.
37114 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37115 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37116 this.container = Roo.get(container);
37119 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37120 init : function(s){
37121 this.basic.init(s);
37124 getElementSize : function(s){
37125 return this.basic.getElementSize(s);
37128 setElementSize : function(s, newSize, onComplete){
37129 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37132 moveSplitter : function(s){
37133 var yes = Roo.bootstrap.SplitBar;
37134 switch(s.placement){
37136 s.el.setX(s.resizingEl.getRight());
37139 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37142 s.el.setY(s.resizingEl.getBottom());
37145 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37152 * Orientation constant - Create a vertical SplitBar
37156 Roo.bootstrap.SplitBar.VERTICAL = 1;
37159 * Orientation constant - Create a horizontal SplitBar
37163 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37166 * Placement constant - The resizing element is to the left of the splitter element
37170 Roo.bootstrap.SplitBar.LEFT = 1;
37173 * Placement constant - The resizing element is to the right of the splitter element
37177 Roo.bootstrap.SplitBar.RIGHT = 2;
37180 * Placement constant - The resizing element is positioned above the splitter element
37184 Roo.bootstrap.SplitBar.TOP = 3;
37187 * Placement constant - The resizing element is positioned under splitter element
37191 Roo.bootstrap.SplitBar.BOTTOM = 4;
37192 Roo.namespace("Roo.bootstrap.layout");/*
37194 * Ext JS Library 1.1.1
37195 * Copyright(c) 2006-2007, Ext JS, LLC.
37197 * Originally Released Under LGPL - original licence link has changed is not relivant.
37200 * <script type="text/javascript">
37204 * @class Roo.bootstrap.layout.Manager
37205 * @extends Roo.bootstrap.Component
37206 * Base class for layout managers.
37208 Roo.bootstrap.layout.Manager = function(config)
37210 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37216 /** false to disable window resize monitoring @type Boolean */
37217 this.monitorWindowResize = true;
37222 * Fires when a layout is performed.
37223 * @param {Roo.LayoutManager} this
37227 * @event regionresized
37228 * Fires when the user resizes a region.
37229 * @param {Roo.LayoutRegion} region The resized region
37230 * @param {Number} newSize The new size (width for east/west, height for north/south)
37232 "regionresized" : true,
37234 * @event regioncollapsed
37235 * Fires when a region is collapsed.
37236 * @param {Roo.LayoutRegion} region The collapsed region
37238 "regioncollapsed" : true,
37240 * @event regionexpanded
37241 * Fires when a region is expanded.
37242 * @param {Roo.LayoutRegion} region The expanded region
37244 "regionexpanded" : true
37246 this.updating = false;
37249 this.el = Roo.get(config.el);
37255 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37260 monitorWindowResize : true,
37266 onRender : function(ct, position)
37269 this.el = Roo.get(ct);
37272 //this.fireEvent('render',this);
37276 initEvents: function()
37280 // ie scrollbar fix
37281 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37282 document.body.scroll = "no";
37283 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37284 this.el.position('relative');
37286 this.id = this.el.id;
37287 this.el.addClass("roo-layout-container");
37288 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37289 if(this.el.dom != document.body ) {
37290 this.el.on('resize', this.layout,this);
37291 this.el.on('show', this.layout,this);
37297 * Returns true if this layout is currently being updated
37298 * @return {Boolean}
37300 isUpdating : function(){
37301 return this.updating;
37305 * Suspend the LayoutManager from doing auto-layouts while
37306 * making multiple add or remove calls
37308 beginUpdate : function(){
37309 this.updating = true;
37313 * Restore auto-layouts and optionally disable the manager from performing a layout
37314 * @param {Boolean} noLayout true to disable a layout update
37316 endUpdate : function(noLayout){
37317 this.updating = false;
37323 layout: function(){
37327 onRegionResized : function(region, newSize){
37328 this.fireEvent("regionresized", region, newSize);
37332 onRegionCollapsed : function(region){
37333 this.fireEvent("regioncollapsed", region);
37336 onRegionExpanded : function(region){
37337 this.fireEvent("regionexpanded", region);
37341 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37342 * performs box-model adjustments.
37343 * @return {Object} The size as an object {width: (the width), height: (the height)}
37345 getViewSize : function()
37348 if(this.el.dom != document.body){
37349 size = this.el.getSize();
37351 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37353 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37354 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37359 * Returns the Element this layout is bound to.
37360 * @return {Roo.Element}
37362 getEl : function(){
37367 * Returns the specified region.
37368 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37369 * @return {Roo.LayoutRegion}
37371 getRegion : function(target){
37372 return this.regions[target.toLowerCase()];
37375 onWindowResize : function(){
37376 if(this.monitorWindowResize){
37383 * Ext JS Library 1.1.1
37384 * Copyright(c) 2006-2007, Ext JS, LLC.
37386 * Originally Released Under LGPL - original licence link has changed is not relivant.
37389 * <script type="text/javascript">
37392 * @class Roo.bootstrap.layout.Border
37393 * @extends Roo.bootstrap.layout.Manager
37394 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37395 * please see: examples/bootstrap/nested.html<br><br>
37397 <b>The container the layout is rendered into can be either the body element or any other element.
37398 If it is not the body element, the container needs to either be an absolute positioned element,
37399 or you will need to add "position:relative" to the css of the container. You will also need to specify
37400 the container size if it is not the body element.</b>
37403 * Create a new Border
37404 * @param {Object} config Configuration options
37406 Roo.bootstrap.layout.Border = function(config){
37407 config = config || {};
37408 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37412 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37413 if(config[region]){
37414 config[region].region = region;
37415 this.addRegion(config[region]);
37421 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37423 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37425 parent : false, // this might point to a 'nest' or a ???
37428 * Creates and adds a new region if it doesn't already exist.
37429 * @param {String} target The target region key (north, south, east, west or center).
37430 * @param {Object} config The regions config object
37431 * @return {BorderLayoutRegion} The new region
37433 addRegion : function(config)
37435 if(!this.regions[config.region]){
37436 var r = this.factory(config);
37437 this.bindRegion(r);
37439 return this.regions[config.region];
37443 bindRegion : function(r){
37444 this.regions[r.config.region] = r;
37446 r.on("visibilitychange", this.layout, this);
37447 r.on("paneladded", this.layout, this);
37448 r.on("panelremoved", this.layout, this);
37449 r.on("invalidated", this.layout, this);
37450 r.on("resized", this.onRegionResized, this);
37451 r.on("collapsed", this.onRegionCollapsed, this);
37452 r.on("expanded", this.onRegionExpanded, this);
37456 * Performs a layout update.
37458 layout : function()
37460 if(this.updating) {
37464 // render all the rebions if they have not been done alreayd?
37465 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37466 if(this.regions[region] && !this.regions[region].bodyEl){
37467 this.regions[region].onRender(this.el)
37471 var size = this.getViewSize();
37472 var w = size.width;
37473 var h = size.height;
37478 //var x = 0, y = 0;
37480 var rs = this.regions;
37481 var north = rs["north"];
37482 var south = rs["south"];
37483 var west = rs["west"];
37484 var east = rs["east"];
37485 var center = rs["center"];
37486 //if(this.hideOnLayout){ // not supported anymore
37487 //c.el.setStyle("display", "none");
37489 if(north && north.isVisible()){
37490 var b = north.getBox();
37491 var m = north.getMargins();
37492 b.width = w - (m.left+m.right);
37495 centerY = b.height + b.y + m.bottom;
37496 centerH -= centerY;
37497 north.updateBox(this.safeBox(b));
37499 if(south && south.isVisible()){
37500 var b = south.getBox();
37501 var m = south.getMargins();
37502 b.width = w - (m.left+m.right);
37504 var totalHeight = (b.height + m.top + m.bottom);
37505 b.y = h - totalHeight + m.top;
37506 centerH -= totalHeight;
37507 south.updateBox(this.safeBox(b));
37509 if(west && west.isVisible()){
37510 var b = west.getBox();
37511 var m = west.getMargins();
37512 b.height = centerH - (m.top+m.bottom);
37514 b.y = centerY + m.top;
37515 var totalWidth = (b.width + m.left + m.right);
37516 centerX += totalWidth;
37517 centerW -= totalWidth;
37518 west.updateBox(this.safeBox(b));
37520 if(east && east.isVisible()){
37521 var b = east.getBox();
37522 var m = east.getMargins();
37523 b.height = centerH - (m.top+m.bottom);
37524 var totalWidth = (b.width + m.left + m.right);
37525 b.x = w - totalWidth + m.left;
37526 b.y = centerY + m.top;
37527 centerW -= totalWidth;
37528 east.updateBox(this.safeBox(b));
37531 var m = center.getMargins();
37533 x: centerX + m.left,
37534 y: centerY + m.top,
37535 width: centerW - (m.left+m.right),
37536 height: centerH - (m.top+m.bottom)
37538 //if(this.hideOnLayout){
37539 //center.el.setStyle("display", "block");
37541 center.updateBox(this.safeBox(centerBox));
37544 this.fireEvent("layout", this);
37548 safeBox : function(box){
37549 box.width = Math.max(0, box.width);
37550 box.height = Math.max(0, box.height);
37555 * Adds a ContentPanel (or subclass) to this layout.
37556 * @param {String} target The target region key (north, south, east, west or center).
37557 * @param {Roo.ContentPanel} panel The panel to add
37558 * @return {Roo.ContentPanel} The added panel
37560 add : function(target, panel){
37562 target = target.toLowerCase();
37563 return this.regions[target].add(panel);
37567 * Remove a ContentPanel (or subclass) to this layout.
37568 * @param {String} target The target region key (north, south, east, west or center).
37569 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37570 * @return {Roo.ContentPanel} The removed panel
37572 remove : function(target, panel){
37573 target = target.toLowerCase();
37574 return this.regions[target].remove(panel);
37578 * Searches all regions for a panel with the specified id
37579 * @param {String} panelId
37580 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37582 findPanel : function(panelId){
37583 var rs = this.regions;
37584 for(var target in rs){
37585 if(typeof rs[target] != "function"){
37586 var p = rs[target].getPanel(panelId);
37596 * Searches all regions for a panel with the specified id and activates (shows) it.
37597 * @param {String/ContentPanel} panelId The panels id or the panel itself
37598 * @return {Roo.ContentPanel} The shown panel or null
37600 showPanel : function(panelId) {
37601 var rs = this.regions;
37602 for(var target in rs){
37603 var r = rs[target];
37604 if(typeof r != "function"){
37605 if(r.hasPanel(panelId)){
37606 return r.showPanel(panelId);
37614 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37615 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37618 restoreState : function(provider){
37620 provider = Roo.state.Manager;
37622 var sm = new Roo.LayoutStateManager();
37623 sm.init(this, provider);
37629 * Adds a xtype elements to the layout.
37633 xtype : 'ContentPanel',
37640 xtype : 'NestedLayoutPanel',
37646 items : [ ... list of content panels or nested layout panels.. ]
37650 * @param {Object} cfg Xtype definition of item to add.
37652 addxtype : function(cfg)
37654 // basically accepts a pannel...
37655 // can accept a layout region..!?!?
37656 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37659 // theory? children can only be panels??
37661 //if (!cfg.xtype.match(/Panel$/)) {
37666 if (typeof(cfg.region) == 'undefined') {
37667 Roo.log("Failed to add Panel, region was not set");
37671 var region = cfg.region;
37677 xitems = cfg.items;
37682 if ( region == 'center') {
37683 Roo.log("Center: " + cfg.title);
37689 case 'Content': // ContentPanel (el, cfg)
37690 case 'Scroll': // ContentPanel (el, cfg)
37692 cfg.autoCreate = cfg.autoCreate || true;
37693 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37695 // var el = this.el.createChild();
37696 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37699 this.add(region, ret);
37703 case 'TreePanel': // our new panel!
37704 cfg.el = this.el.createChild();
37705 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37706 this.add(region, ret);
37711 // create a new Layout (which is a Border Layout...
37713 var clayout = cfg.layout;
37714 clayout.el = this.el.createChild();
37715 clayout.items = clayout.items || [];
37719 // replace this exitems with the clayout ones..
37720 xitems = clayout.items;
37722 // force background off if it's in center...
37723 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37724 cfg.background = false;
37726 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37729 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37730 //console.log('adding nested layout panel ' + cfg.toSource());
37731 this.add(region, ret);
37732 nb = {}; /// find first...
37737 // needs grid and region
37739 //var el = this.getRegion(region).el.createChild();
37741 *var el = this.el.createChild();
37742 // create the grid first...
37743 cfg.grid.container = el;
37744 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37747 if (region == 'center' && this.active ) {
37748 cfg.background = false;
37751 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37753 this.add(region, ret);
37755 if (cfg.background) {
37756 // render grid on panel activation (if panel background)
37757 ret.on('activate', function(gp) {
37758 if (!gp.grid.rendered) {
37759 // gp.grid.render(el);
37763 // cfg.grid.render(el);
37769 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37770 // it was the old xcomponent building that caused this before.
37771 // espeically if border is the top element in the tree.
37781 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37783 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37784 this.add(region, ret);
37788 throw "Can not add '" + cfg.xtype + "' to Border";
37794 this.beginUpdate();
37798 Roo.each(xitems, function(i) {
37799 region = nb && i.region ? i.region : false;
37801 var add = ret.addxtype(i);
37804 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37805 if (!i.background) {
37806 abn[region] = nb[region] ;
37813 // make the last non-background panel active..
37814 //if (nb) { Roo.log(abn); }
37817 for(var r in abn) {
37818 region = this.getRegion(r);
37820 // tried using nb[r], but it does not work..
37822 region.showPanel(abn[r]);
37833 factory : function(cfg)
37836 var validRegions = Roo.bootstrap.layout.Border.regions;
37838 var target = cfg.region;
37841 var r = Roo.bootstrap.layout;
37845 return new r.North(cfg);
37847 return new r.South(cfg);
37849 return new r.East(cfg);
37851 return new r.West(cfg);
37853 return new r.Center(cfg);
37855 throw 'Layout region "'+target+'" not supported.';
37862 * Ext JS Library 1.1.1
37863 * Copyright(c) 2006-2007, Ext JS, LLC.
37865 * Originally Released Under LGPL - original licence link has changed is not relivant.
37868 * <script type="text/javascript">
37872 * @class Roo.bootstrap.layout.Basic
37873 * @extends Roo.util.Observable
37874 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37875 * and does not have a titlebar, tabs or any other features. All it does is size and position
37876 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37877 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37878 * @cfg {string} region the region that it inhabits..
37879 * @cfg {bool} skipConfig skip config?
37883 Roo.bootstrap.layout.Basic = function(config){
37885 this.mgr = config.mgr;
37887 this.position = config.region;
37889 var skipConfig = config.skipConfig;
37893 * @scope Roo.BasicLayoutRegion
37897 * @event beforeremove
37898 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37899 * @param {Roo.LayoutRegion} this
37900 * @param {Roo.ContentPanel} panel The panel
37901 * @param {Object} e The cancel event object
37903 "beforeremove" : true,
37905 * @event invalidated
37906 * Fires when the layout for this region is changed.
37907 * @param {Roo.LayoutRegion} this
37909 "invalidated" : true,
37911 * @event visibilitychange
37912 * Fires when this region is shown or hidden
37913 * @param {Roo.LayoutRegion} this
37914 * @param {Boolean} visibility true or false
37916 "visibilitychange" : true,
37918 * @event paneladded
37919 * Fires when a panel is added.
37920 * @param {Roo.LayoutRegion} this
37921 * @param {Roo.ContentPanel} panel The panel
37923 "paneladded" : true,
37925 * @event panelremoved
37926 * Fires when a panel is removed.
37927 * @param {Roo.LayoutRegion} this
37928 * @param {Roo.ContentPanel} panel The panel
37930 "panelremoved" : true,
37932 * @event beforecollapse
37933 * Fires when this region before collapse.
37934 * @param {Roo.LayoutRegion} this
37936 "beforecollapse" : true,
37939 * Fires when this region is collapsed.
37940 * @param {Roo.LayoutRegion} this
37942 "collapsed" : true,
37945 * Fires when this region is expanded.
37946 * @param {Roo.LayoutRegion} this
37951 * Fires when this region is slid into view.
37952 * @param {Roo.LayoutRegion} this
37954 "slideshow" : true,
37957 * Fires when this region slides out of view.
37958 * @param {Roo.LayoutRegion} this
37960 "slidehide" : true,
37962 * @event panelactivated
37963 * Fires when a panel is activated.
37964 * @param {Roo.LayoutRegion} this
37965 * @param {Roo.ContentPanel} panel The activated panel
37967 "panelactivated" : true,
37970 * Fires when the user resizes this region.
37971 * @param {Roo.LayoutRegion} this
37972 * @param {Number} newSize The new size (width for east/west, height for north/south)
37976 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37977 this.panels = new Roo.util.MixedCollection();
37978 this.panels.getKey = this.getPanelId.createDelegate(this);
37980 this.activePanel = null;
37981 // ensure listeners are added...
37983 if (config.listeners || config.events) {
37984 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37985 listeners : config.listeners || {},
37986 events : config.events || {}
37990 if(skipConfig !== true){
37991 this.applyConfig(config);
37995 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37997 getPanelId : function(p){
38001 applyConfig : function(config){
38002 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38003 this.config = config;
38008 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38009 * the width, for horizontal (north, south) the height.
38010 * @param {Number} newSize The new width or height
38012 resizeTo : function(newSize){
38013 var el = this.el ? this.el :
38014 (this.activePanel ? this.activePanel.getEl() : null);
38016 switch(this.position){
38019 el.setWidth(newSize);
38020 this.fireEvent("resized", this, newSize);
38024 el.setHeight(newSize);
38025 this.fireEvent("resized", this, newSize);
38031 getBox : function(){
38032 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38035 getMargins : function(){
38036 return this.margins;
38039 updateBox : function(box){
38041 var el = this.activePanel.getEl();
38042 el.dom.style.left = box.x + "px";
38043 el.dom.style.top = box.y + "px";
38044 this.activePanel.setSize(box.width, box.height);
38048 * Returns the container element for this region.
38049 * @return {Roo.Element}
38051 getEl : function(){
38052 return this.activePanel;
38056 * Returns true if this region is currently visible.
38057 * @return {Boolean}
38059 isVisible : function(){
38060 return this.activePanel ? true : false;
38063 setActivePanel : function(panel){
38064 panel = this.getPanel(panel);
38065 if(this.activePanel && this.activePanel != panel){
38066 this.activePanel.setActiveState(false);
38067 this.activePanel.getEl().setLeftTop(-10000,-10000);
38069 this.activePanel = panel;
38070 panel.setActiveState(true);
38072 panel.setSize(this.box.width, this.box.height);
38074 this.fireEvent("panelactivated", this, panel);
38075 this.fireEvent("invalidated");
38079 * Show the specified panel.
38080 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38081 * @return {Roo.ContentPanel} The shown panel or null
38083 showPanel : function(panel){
38084 panel = this.getPanel(panel);
38086 this.setActivePanel(panel);
38092 * Get the active panel for this region.
38093 * @return {Roo.ContentPanel} The active panel or null
38095 getActivePanel : function(){
38096 return this.activePanel;
38100 * Add the passed ContentPanel(s)
38101 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38102 * @return {Roo.ContentPanel} The panel added (if only one was added)
38104 add : function(panel){
38105 if(arguments.length > 1){
38106 for(var i = 0, len = arguments.length; i < len; i++) {
38107 this.add(arguments[i]);
38111 if(this.hasPanel(panel)){
38112 this.showPanel(panel);
38115 var el = panel.getEl();
38116 if(el.dom.parentNode != this.mgr.el.dom){
38117 this.mgr.el.dom.appendChild(el.dom);
38119 if(panel.setRegion){
38120 panel.setRegion(this);
38122 this.panels.add(panel);
38123 el.setStyle("position", "absolute");
38124 if(!panel.background){
38125 this.setActivePanel(panel);
38126 if(this.config.initialSize && this.panels.getCount()==1){
38127 this.resizeTo(this.config.initialSize);
38130 this.fireEvent("paneladded", this, panel);
38135 * Returns true if the panel is in this region.
38136 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38137 * @return {Boolean}
38139 hasPanel : function(panel){
38140 if(typeof panel == "object"){ // must be panel obj
38141 panel = panel.getId();
38143 return this.getPanel(panel) ? true : false;
38147 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38148 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38149 * @param {Boolean} preservePanel Overrides the config preservePanel option
38150 * @return {Roo.ContentPanel} The panel that was removed
38152 remove : function(panel, preservePanel){
38153 panel = this.getPanel(panel);
38158 this.fireEvent("beforeremove", this, panel, e);
38159 if(e.cancel === true){
38162 var panelId = panel.getId();
38163 this.panels.removeKey(panelId);
38168 * Returns the panel specified or null if it's not in this region.
38169 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38170 * @return {Roo.ContentPanel}
38172 getPanel : function(id){
38173 if(typeof id == "object"){ // must be panel obj
38176 return this.panels.get(id);
38180 * Returns this regions position (north/south/east/west/center).
38183 getPosition: function(){
38184 return this.position;
38188 * Ext JS Library 1.1.1
38189 * Copyright(c) 2006-2007, Ext JS, LLC.
38191 * Originally Released Under LGPL - original licence link has changed is not relivant.
38194 * <script type="text/javascript">
38198 * @class Roo.bootstrap.layout.Region
38199 * @extends Roo.bootstrap.layout.Basic
38200 * This class represents a region in a layout manager.
38202 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38203 * @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})
38204 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38205 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38206 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38207 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38208 * @cfg {String} title The title for the region (overrides panel titles)
38209 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38210 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38211 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38212 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38213 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38214 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38215 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38216 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38217 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38218 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38220 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38221 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38222 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38223 * @cfg {Number} width For East/West panels
38224 * @cfg {Number} height For North/South panels
38225 * @cfg {Boolean} split To show the splitter
38226 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38228 * @cfg {string} cls Extra CSS classes to add to region
38230 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38231 * @cfg {string} region the region that it inhabits..
38234 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38235 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38237 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38238 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38239 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38241 Roo.bootstrap.layout.Region = function(config)
38243 this.applyConfig(config);
38245 var mgr = config.mgr;
38246 var pos = config.region;
38247 config.skipConfig = true;
38248 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38251 this.onRender(mgr.el);
38254 this.visible = true;
38255 this.collapsed = false;
38256 this.unrendered_panels = [];
38259 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38261 position: '', // set by wrapper (eg. north/south etc..)
38262 unrendered_panels : null, // unrendered panels.
38264 tabPosition : false,
38266 mgr: false, // points to 'Border'
38269 createBody : function(){
38270 /** This region's body element
38271 * @type Roo.Element */
38272 this.bodyEl = this.el.createChild({
38274 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38278 onRender: function(ctr, pos)
38280 var dh = Roo.DomHelper;
38281 /** This region's container element
38282 * @type Roo.Element */
38283 this.el = dh.append(ctr.dom, {
38285 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38287 /** This region's title element
38288 * @type Roo.Element */
38290 this.titleEl = dh.append(this.el.dom, {
38292 unselectable: "on",
38293 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38295 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38296 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38300 this.titleEl.enableDisplayMode();
38301 /** This region's title text element
38302 * @type HTMLElement */
38303 this.titleTextEl = this.titleEl.dom.firstChild;
38304 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38306 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38307 this.closeBtn.enableDisplayMode();
38308 this.closeBtn.on("click", this.closeClicked, this);
38309 this.closeBtn.hide();
38311 this.createBody(this.config);
38312 if(this.config.hideWhenEmpty){
38314 this.on("paneladded", this.validateVisibility, this);
38315 this.on("panelremoved", this.validateVisibility, this);
38317 if(this.autoScroll){
38318 this.bodyEl.setStyle("overflow", "auto");
38320 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38322 //if(c.titlebar !== false){
38323 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38324 this.titleEl.hide();
38326 this.titleEl.show();
38327 if(this.config.title){
38328 this.titleTextEl.innerHTML = this.config.title;
38332 if(this.config.collapsed){
38333 this.collapse(true);
38335 if(this.config.hidden){
38339 if (this.unrendered_panels && this.unrendered_panels.length) {
38340 for (var i =0;i< this.unrendered_panels.length; i++) {
38341 this.add(this.unrendered_panels[i]);
38343 this.unrendered_panels = null;
38349 applyConfig : function(c)
38352 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38353 var dh = Roo.DomHelper;
38354 if(c.titlebar !== false){
38355 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38356 this.collapseBtn.on("click", this.collapse, this);
38357 this.collapseBtn.enableDisplayMode();
38359 if(c.showPin === true || this.showPin){
38360 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38361 this.stickBtn.enableDisplayMode();
38362 this.stickBtn.on("click", this.expand, this);
38363 this.stickBtn.hide();
38368 /** This region's collapsed element
38369 * @type Roo.Element */
38372 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38373 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38376 if(c.floatable !== false){
38377 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38378 this.collapsedEl.on("click", this.collapseClick, this);
38381 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38382 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38383 id: "message", unselectable: "on", style:{"float":"left"}});
38384 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38386 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38387 this.expandBtn.on("click", this.expand, this);
38391 if(this.collapseBtn){
38392 this.collapseBtn.setVisible(c.collapsible == true);
38395 this.cmargins = c.cmargins || this.cmargins ||
38396 (this.position == "west" || this.position == "east" ?
38397 {top: 0, left: 2, right:2, bottom: 0} :
38398 {top: 2, left: 0, right:0, bottom: 2});
38400 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38403 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38405 this.autoScroll = c.autoScroll || false;
38410 this.duration = c.duration || .30;
38411 this.slideDuration = c.slideDuration || .45;
38416 * Returns true if this region is currently visible.
38417 * @return {Boolean}
38419 isVisible : function(){
38420 return this.visible;
38424 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38425 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38427 //setCollapsedTitle : function(title){
38428 // title = title || " ";
38429 // if(this.collapsedTitleTextEl){
38430 // this.collapsedTitleTextEl.innerHTML = title;
38434 getBox : function(){
38436 // if(!this.collapsed){
38437 b = this.el.getBox(false, true);
38439 // b = this.collapsedEl.getBox(false, true);
38444 getMargins : function(){
38445 return this.margins;
38446 //return this.collapsed ? this.cmargins : this.margins;
38449 highlight : function(){
38450 this.el.addClass("x-layout-panel-dragover");
38453 unhighlight : function(){
38454 this.el.removeClass("x-layout-panel-dragover");
38457 updateBox : function(box)
38459 if (!this.bodyEl) {
38460 return; // not rendered yet..
38464 if(!this.collapsed){
38465 this.el.dom.style.left = box.x + "px";
38466 this.el.dom.style.top = box.y + "px";
38467 this.updateBody(box.width, box.height);
38469 this.collapsedEl.dom.style.left = box.x + "px";
38470 this.collapsedEl.dom.style.top = box.y + "px";
38471 this.collapsedEl.setSize(box.width, box.height);
38474 this.tabs.autoSizeTabs();
38478 updateBody : function(w, h)
38481 this.el.setWidth(w);
38482 w -= this.el.getBorderWidth("rl");
38483 if(this.config.adjustments){
38484 w += this.config.adjustments[0];
38487 if(h !== null && h > 0){
38488 this.el.setHeight(h);
38489 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38490 h -= this.el.getBorderWidth("tb");
38491 if(this.config.adjustments){
38492 h += this.config.adjustments[1];
38494 this.bodyEl.setHeight(h);
38496 h = this.tabs.syncHeight(h);
38499 if(this.panelSize){
38500 w = w !== null ? w : this.panelSize.width;
38501 h = h !== null ? h : this.panelSize.height;
38503 if(this.activePanel){
38504 var el = this.activePanel.getEl();
38505 w = w !== null ? w : el.getWidth();
38506 h = h !== null ? h : el.getHeight();
38507 this.panelSize = {width: w, height: h};
38508 this.activePanel.setSize(w, h);
38510 if(Roo.isIE && this.tabs){
38511 this.tabs.el.repaint();
38516 * Returns the container element for this region.
38517 * @return {Roo.Element}
38519 getEl : function(){
38524 * Hides this region.
38527 //if(!this.collapsed){
38528 this.el.dom.style.left = "-2000px";
38531 // this.collapsedEl.dom.style.left = "-2000px";
38532 // this.collapsedEl.hide();
38534 this.visible = false;
38535 this.fireEvent("visibilitychange", this, false);
38539 * Shows this region if it was previously hidden.
38542 //if(!this.collapsed){
38545 // this.collapsedEl.show();
38547 this.visible = true;
38548 this.fireEvent("visibilitychange", this, true);
38551 closeClicked : function(){
38552 if(this.activePanel){
38553 this.remove(this.activePanel);
38557 collapseClick : function(e){
38559 e.stopPropagation();
38562 e.stopPropagation();
38568 * Collapses this region.
38569 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38572 collapse : function(skipAnim, skipCheck = false){
38573 if(this.collapsed) {
38577 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38579 this.collapsed = true;
38581 this.split.el.hide();
38583 if(this.config.animate && skipAnim !== true){
38584 this.fireEvent("invalidated", this);
38585 this.animateCollapse();
38587 this.el.setLocation(-20000,-20000);
38589 this.collapsedEl.show();
38590 this.fireEvent("collapsed", this);
38591 this.fireEvent("invalidated", this);
38597 animateCollapse : function(){
38602 * Expands this region if it was previously collapsed.
38603 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38604 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38607 expand : function(e, skipAnim){
38609 e.stopPropagation();
38611 if(!this.collapsed || this.el.hasActiveFx()) {
38615 this.afterSlideIn();
38618 this.collapsed = false;
38619 if(this.config.animate && skipAnim !== true){
38620 this.animateExpand();
38624 this.split.el.show();
38626 this.collapsedEl.setLocation(-2000,-2000);
38627 this.collapsedEl.hide();
38628 this.fireEvent("invalidated", this);
38629 this.fireEvent("expanded", this);
38633 animateExpand : function(){
38637 initTabs : function()
38639 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38641 var ts = new Roo.bootstrap.panel.Tabs({
38642 el: this.bodyEl.dom,
38644 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38645 disableTooltips: this.config.disableTabTips,
38646 toolbar : this.config.toolbar
38649 if(this.config.hideTabs){
38650 ts.stripWrap.setDisplayed(false);
38653 ts.resizeTabs = this.config.resizeTabs === true;
38654 ts.minTabWidth = this.config.minTabWidth || 40;
38655 ts.maxTabWidth = this.config.maxTabWidth || 250;
38656 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38657 ts.monitorResize = false;
38658 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38659 ts.bodyEl.addClass('roo-layout-tabs-body');
38660 this.panels.each(this.initPanelAsTab, this);
38663 initPanelAsTab : function(panel){
38664 var ti = this.tabs.addTab(
38668 this.config.closeOnTab && panel.isClosable(),
38671 if(panel.tabTip !== undefined){
38672 ti.setTooltip(panel.tabTip);
38674 ti.on("activate", function(){
38675 this.setActivePanel(panel);
38678 if(this.config.closeOnTab){
38679 ti.on("beforeclose", function(t, e){
38681 this.remove(panel);
38685 panel.tabItem = ti;
38690 updatePanelTitle : function(panel, title)
38692 if(this.activePanel == panel){
38693 this.updateTitle(title);
38696 var ti = this.tabs.getTab(panel.getEl().id);
38698 if(panel.tabTip !== undefined){
38699 ti.setTooltip(panel.tabTip);
38704 updateTitle : function(title){
38705 if(this.titleTextEl && !this.config.title){
38706 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38710 setActivePanel : function(panel)
38712 panel = this.getPanel(panel);
38713 if(this.activePanel && this.activePanel != panel){
38714 if(this.activePanel.setActiveState(false) === false){
38718 this.activePanel = panel;
38719 panel.setActiveState(true);
38720 if(this.panelSize){
38721 panel.setSize(this.panelSize.width, this.panelSize.height);
38724 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38726 this.updateTitle(panel.getTitle());
38728 this.fireEvent("invalidated", this);
38730 this.fireEvent("panelactivated", this, panel);
38734 * Shows the specified panel.
38735 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38736 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38738 showPanel : function(panel)
38740 panel = this.getPanel(panel);
38743 var tab = this.tabs.getTab(panel.getEl().id);
38744 if(tab.isHidden()){
38745 this.tabs.unhideTab(tab.id);
38749 this.setActivePanel(panel);
38756 * Get the active panel for this region.
38757 * @return {Roo.ContentPanel} The active panel or null
38759 getActivePanel : function(){
38760 return this.activePanel;
38763 validateVisibility : function(){
38764 if(this.panels.getCount() < 1){
38765 this.updateTitle(" ");
38766 this.closeBtn.hide();
38769 if(!this.isVisible()){
38776 * Adds the passed ContentPanel(s) to this region.
38777 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38778 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38780 add : function(panel)
38782 if(arguments.length > 1){
38783 for(var i = 0, len = arguments.length; i < len; i++) {
38784 this.add(arguments[i]);
38789 // if we have not been rendered yet, then we can not really do much of this..
38790 if (!this.bodyEl) {
38791 this.unrendered_panels.push(panel);
38798 if(this.hasPanel(panel)){
38799 this.showPanel(panel);
38802 panel.setRegion(this);
38803 this.panels.add(panel);
38804 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38805 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38806 // and hide them... ???
38807 this.bodyEl.dom.appendChild(panel.getEl().dom);
38808 if(panel.background !== true){
38809 this.setActivePanel(panel);
38811 this.fireEvent("paneladded", this, panel);
38818 this.initPanelAsTab(panel);
38822 if(panel.background !== true){
38823 this.tabs.activate(panel.getEl().id);
38825 this.fireEvent("paneladded", this, panel);
38830 * Hides the tab for the specified panel.
38831 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38833 hidePanel : function(panel){
38834 if(this.tabs && (panel = this.getPanel(panel))){
38835 this.tabs.hideTab(panel.getEl().id);
38840 * Unhides the tab for a previously hidden panel.
38841 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38843 unhidePanel : function(panel){
38844 if(this.tabs && (panel = this.getPanel(panel))){
38845 this.tabs.unhideTab(panel.getEl().id);
38849 clearPanels : function(){
38850 while(this.panels.getCount() > 0){
38851 this.remove(this.panels.first());
38856 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38857 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38858 * @param {Boolean} preservePanel Overrides the config preservePanel option
38859 * @return {Roo.ContentPanel} The panel that was removed
38861 remove : function(panel, preservePanel)
38863 panel = this.getPanel(panel);
38868 this.fireEvent("beforeremove", this, panel, e);
38869 if(e.cancel === true){
38872 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38873 var panelId = panel.getId();
38874 this.panels.removeKey(panelId);
38876 document.body.appendChild(panel.getEl().dom);
38879 this.tabs.removeTab(panel.getEl().id);
38880 }else if (!preservePanel){
38881 this.bodyEl.dom.removeChild(panel.getEl().dom);
38883 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38884 var p = this.panels.first();
38885 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38886 tempEl.appendChild(p.getEl().dom);
38887 this.bodyEl.update("");
38888 this.bodyEl.dom.appendChild(p.getEl().dom);
38890 this.updateTitle(p.getTitle());
38892 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38893 this.setActivePanel(p);
38895 panel.setRegion(null);
38896 if(this.activePanel == panel){
38897 this.activePanel = null;
38899 if(this.config.autoDestroy !== false && preservePanel !== true){
38900 try{panel.destroy();}catch(e){}
38902 this.fireEvent("panelremoved", this, panel);
38907 * Returns the TabPanel component used by this region
38908 * @return {Roo.TabPanel}
38910 getTabs : function(){
38914 createTool : function(parentEl, className){
38915 var btn = Roo.DomHelper.append(parentEl, {
38917 cls: "x-layout-tools-button",
38920 cls: "roo-layout-tools-button-inner " + className,
38924 btn.addClassOnOver("roo-layout-tools-button-over");
38929 * Ext JS Library 1.1.1
38930 * Copyright(c) 2006-2007, Ext JS, LLC.
38932 * Originally Released Under LGPL - original licence link has changed is not relivant.
38935 * <script type="text/javascript">
38941 * @class Roo.SplitLayoutRegion
38942 * @extends Roo.LayoutRegion
38943 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38945 Roo.bootstrap.layout.Split = function(config){
38946 this.cursor = config.cursor;
38947 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38950 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38952 splitTip : "Drag to resize.",
38953 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38954 useSplitTips : false,
38956 applyConfig : function(config){
38957 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38960 onRender : function(ctr,pos) {
38962 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38963 if(!this.config.split){
38968 var splitEl = Roo.DomHelper.append(ctr.dom, {
38970 id: this.el.id + "-split",
38971 cls: "roo-layout-split roo-layout-split-"+this.position,
38974 /** The SplitBar for this region
38975 * @type Roo.SplitBar */
38976 // does not exist yet...
38977 Roo.log([this.position, this.orientation]);
38979 this.split = new Roo.bootstrap.SplitBar({
38980 dragElement : splitEl,
38981 resizingElement: this.el,
38982 orientation : this.orientation
38985 this.split.on("moved", this.onSplitMove, this);
38986 this.split.useShim = this.config.useShim === true;
38987 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38988 if(this.useSplitTips){
38989 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38991 //if(config.collapsible){
38992 // this.split.el.on("dblclick", this.collapse, this);
38995 if(typeof this.config.minSize != "undefined"){
38996 this.split.minSize = this.config.minSize;
38998 if(typeof this.config.maxSize != "undefined"){
38999 this.split.maxSize = this.config.maxSize;
39001 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39002 this.hideSplitter();
39007 getHMaxSize : function(){
39008 var cmax = this.config.maxSize || 10000;
39009 var center = this.mgr.getRegion("center");
39010 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39013 getVMaxSize : function(){
39014 var cmax = this.config.maxSize || 10000;
39015 var center = this.mgr.getRegion("center");
39016 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39019 onSplitMove : function(split, newSize){
39020 this.fireEvent("resized", this, newSize);
39024 * Returns the {@link Roo.SplitBar} for this region.
39025 * @return {Roo.SplitBar}
39027 getSplitBar : function(){
39032 this.hideSplitter();
39033 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39036 hideSplitter : function(){
39038 this.split.el.setLocation(-2000,-2000);
39039 this.split.el.hide();
39045 this.split.el.show();
39047 Roo.bootstrap.layout.Split.superclass.show.call(this);
39050 beforeSlide: function(){
39051 if(Roo.isGecko){// firefox overflow auto bug workaround
39052 this.bodyEl.clip();
39054 this.tabs.bodyEl.clip();
39056 if(this.activePanel){
39057 this.activePanel.getEl().clip();
39059 if(this.activePanel.beforeSlide){
39060 this.activePanel.beforeSlide();
39066 afterSlide : function(){
39067 if(Roo.isGecko){// firefox overflow auto bug workaround
39068 this.bodyEl.unclip();
39070 this.tabs.bodyEl.unclip();
39072 if(this.activePanel){
39073 this.activePanel.getEl().unclip();
39074 if(this.activePanel.afterSlide){
39075 this.activePanel.afterSlide();
39081 initAutoHide : function(){
39082 if(this.autoHide !== false){
39083 if(!this.autoHideHd){
39084 var st = new Roo.util.DelayedTask(this.slideIn, this);
39085 this.autoHideHd = {
39086 "mouseout": function(e){
39087 if(!e.within(this.el, true)){
39091 "mouseover" : function(e){
39097 this.el.on(this.autoHideHd);
39101 clearAutoHide : function(){
39102 if(this.autoHide !== false){
39103 this.el.un("mouseout", this.autoHideHd.mouseout);
39104 this.el.un("mouseover", this.autoHideHd.mouseover);
39108 clearMonitor : function(){
39109 Roo.get(document).un("click", this.slideInIf, this);
39112 // these names are backwards but not changed for compat
39113 slideOut : function(){
39114 if(this.isSlid || this.el.hasActiveFx()){
39117 this.isSlid = true;
39118 if(this.collapseBtn){
39119 this.collapseBtn.hide();
39121 this.closeBtnState = this.closeBtn.getStyle('display');
39122 this.closeBtn.hide();
39124 this.stickBtn.show();
39127 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39128 this.beforeSlide();
39129 this.el.setStyle("z-index", 10001);
39130 this.el.slideIn(this.getSlideAnchor(), {
39131 callback: function(){
39133 this.initAutoHide();
39134 Roo.get(document).on("click", this.slideInIf, this);
39135 this.fireEvent("slideshow", this);
39142 afterSlideIn : function(){
39143 this.clearAutoHide();
39144 this.isSlid = false;
39145 this.clearMonitor();
39146 this.el.setStyle("z-index", "");
39147 if(this.collapseBtn){
39148 this.collapseBtn.show();
39150 this.closeBtn.setStyle('display', this.closeBtnState);
39152 this.stickBtn.hide();
39154 this.fireEvent("slidehide", this);
39157 slideIn : function(cb){
39158 if(!this.isSlid || this.el.hasActiveFx()){
39162 this.isSlid = false;
39163 this.beforeSlide();
39164 this.el.slideOut(this.getSlideAnchor(), {
39165 callback: function(){
39166 this.el.setLeftTop(-10000, -10000);
39168 this.afterSlideIn();
39176 slideInIf : function(e){
39177 if(!e.within(this.el)){
39182 animateCollapse : function(){
39183 this.beforeSlide();
39184 this.el.setStyle("z-index", 20000);
39185 var anchor = this.getSlideAnchor();
39186 this.el.slideOut(anchor, {
39187 callback : function(){
39188 this.el.setStyle("z-index", "");
39189 this.collapsedEl.slideIn(anchor, {duration:.3});
39191 this.el.setLocation(-10000,-10000);
39193 this.fireEvent("collapsed", this);
39200 animateExpand : function(){
39201 this.beforeSlide();
39202 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39203 this.el.setStyle("z-index", 20000);
39204 this.collapsedEl.hide({
39207 this.el.slideIn(this.getSlideAnchor(), {
39208 callback : function(){
39209 this.el.setStyle("z-index", "");
39212 this.split.el.show();
39214 this.fireEvent("invalidated", this);
39215 this.fireEvent("expanded", this);
39243 getAnchor : function(){
39244 return this.anchors[this.position];
39247 getCollapseAnchor : function(){
39248 return this.canchors[this.position];
39251 getSlideAnchor : function(){
39252 return this.sanchors[this.position];
39255 getAlignAdj : function(){
39256 var cm = this.cmargins;
39257 switch(this.position){
39273 getExpandAdj : function(){
39274 var c = this.collapsedEl, cm = this.cmargins;
39275 switch(this.position){
39277 return [-(cm.right+c.getWidth()+cm.left), 0];
39280 return [cm.right+c.getWidth()+cm.left, 0];
39283 return [0, -(cm.top+cm.bottom+c.getHeight())];
39286 return [0, cm.top+cm.bottom+c.getHeight()];
39292 * Ext JS Library 1.1.1
39293 * Copyright(c) 2006-2007, Ext JS, LLC.
39295 * Originally Released Under LGPL - original licence link has changed is not relivant.
39298 * <script type="text/javascript">
39301 * These classes are private internal classes
39303 Roo.bootstrap.layout.Center = function(config){
39304 config.region = "center";
39305 Roo.bootstrap.layout.Region.call(this, config);
39306 this.visible = true;
39307 this.minWidth = config.minWidth || 20;
39308 this.minHeight = config.minHeight || 20;
39311 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39313 // center panel can't be hidden
39317 // center panel can't be hidden
39320 getMinWidth: function(){
39321 return this.minWidth;
39324 getMinHeight: function(){
39325 return this.minHeight;
39339 Roo.bootstrap.layout.North = function(config)
39341 config.region = 'north';
39342 config.cursor = 'n-resize';
39344 Roo.bootstrap.layout.Split.call(this, config);
39348 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39349 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39350 this.split.el.addClass("roo-layout-split-v");
39352 //var size = config.initialSize || config.height;
39353 //if(this.el && typeof size != "undefined"){
39354 // this.el.setHeight(size);
39357 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39359 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39362 onRender : function(ctr, pos)
39364 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39365 var size = this.config.initialSize || this.config.height;
39366 if(this.el && typeof size != "undefined"){
39367 this.el.setHeight(size);
39372 getBox : function(){
39373 if(this.collapsed){
39374 return this.collapsedEl.getBox();
39376 var box = this.el.getBox();
39378 box.height += this.split.el.getHeight();
39383 updateBox : function(box){
39384 if(this.split && !this.collapsed){
39385 box.height -= this.split.el.getHeight();
39386 this.split.el.setLeft(box.x);
39387 this.split.el.setTop(box.y+box.height);
39388 this.split.el.setWidth(box.width);
39390 if(this.collapsed){
39391 this.updateBody(box.width, null);
39393 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39401 Roo.bootstrap.layout.South = function(config){
39402 config.region = 'south';
39403 config.cursor = 's-resize';
39404 Roo.bootstrap.layout.Split.call(this, config);
39406 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39407 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39408 this.split.el.addClass("roo-layout-split-v");
39413 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39414 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39416 onRender : function(ctr, pos)
39418 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39419 var size = this.config.initialSize || this.config.height;
39420 if(this.el && typeof size != "undefined"){
39421 this.el.setHeight(size);
39426 getBox : function(){
39427 if(this.collapsed){
39428 return this.collapsedEl.getBox();
39430 var box = this.el.getBox();
39432 var sh = this.split.el.getHeight();
39439 updateBox : function(box){
39440 if(this.split && !this.collapsed){
39441 var sh = this.split.el.getHeight();
39444 this.split.el.setLeft(box.x);
39445 this.split.el.setTop(box.y-sh);
39446 this.split.el.setWidth(box.width);
39448 if(this.collapsed){
39449 this.updateBody(box.width, null);
39451 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39455 Roo.bootstrap.layout.East = function(config){
39456 config.region = "east";
39457 config.cursor = "e-resize";
39458 Roo.bootstrap.layout.Split.call(this, config);
39460 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39461 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39462 this.split.el.addClass("roo-layout-split-h");
39466 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39467 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39469 onRender : function(ctr, pos)
39471 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39472 var size = this.config.initialSize || this.config.width;
39473 if(this.el && typeof size != "undefined"){
39474 this.el.setWidth(size);
39479 getBox : function(){
39480 if(this.collapsed){
39481 return this.collapsedEl.getBox();
39483 var box = this.el.getBox();
39485 var sw = this.split.el.getWidth();
39492 updateBox : function(box){
39493 if(this.split && !this.collapsed){
39494 var sw = this.split.el.getWidth();
39496 this.split.el.setLeft(box.x);
39497 this.split.el.setTop(box.y);
39498 this.split.el.setHeight(box.height);
39501 if(this.collapsed){
39502 this.updateBody(null, box.height);
39504 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39508 Roo.bootstrap.layout.West = function(config){
39509 config.region = "west";
39510 config.cursor = "w-resize";
39512 Roo.bootstrap.layout.Split.call(this, config);
39514 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39515 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39516 this.split.el.addClass("roo-layout-split-h");
39520 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39521 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39523 onRender: function(ctr, pos)
39525 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39526 var size = this.config.initialSize || this.config.width;
39527 if(typeof size != "undefined"){
39528 this.el.setWidth(size);
39532 getBox : function(){
39533 if(this.collapsed){
39534 return this.collapsedEl.getBox();
39536 var box = this.el.getBox();
39537 if (box.width == 0) {
39538 box.width = this.config.width; // kludge?
39541 box.width += this.split.el.getWidth();
39546 updateBox : function(box){
39547 if(this.split && !this.collapsed){
39548 var sw = this.split.el.getWidth();
39550 this.split.el.setLeft(box.x+box.width);
39551 this.split.el.setTop(box.y);
39552 this.split.el.setHeight(box.height);
39554 if(this.collapsed){
39555 this.updateBody(null, box.height);
39557 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39559 });Roo.namespace("Roo.bootstrap.panel");/*
39561 * Ext JS Library 1.1.1
39562 * Copyright(c) 2006-2007, Ext JS, LLC.
39564 * Originally Released Under LGPL - original licence link has changed is not relivant.
39567 * <script type="text/javascript">
39570 * @class Roo.ContentPanel
39571 * @extends Roo.util.Observable
39572 * A basic ContentPanel element.
39573 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39574 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39575 * @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
39576 * @cfg {Boolean} closable True if the panel can be closed/removed
39577 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39578 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39579 * @cfg {Toolbar} toolbar A toolbar for this panel
39580 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39581 * @cfg {String} title The title for this panel
39582 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39583 * @cfg {String} url Calls {@link #setUrl} with this value
39584 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39585 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39586 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39587 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39588 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39589 * @cfg {Boolean} badges render the badges
39590 * @cfg {String} cls extra classes to use
39591 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39594 * Create a new ContentPanel.
39595 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39596 * @param {String/Object} config A string to set only the title or a config object
39597 * @param {String} content (optional) Set the HTML content for this panel
39598 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39600 Roo.bootstrap.panel.Content = function( config){
39602 this.tpl = config.tpl || false;
39604 var el = config.el;
39605 var content = config.content;
39607 if(config.autoCreate){ // xtype is available if this is called from factory
39610 this.el = Roo.get(el);
39611 if(!this.el && config && config.autoCreate){
39612 if(typeof config.autoCreate == "object"){
39613 if(!config.autoCreate.id){
39614 config.autoCreate.id = config.id||el;
39616 this.el = Roo.DomHelper.append(document.body,
39617 config.autoCreate, true);
39621 cls: (config.cls || '') +
39622 (config.background ? ' bg-' + config.background : '') +
39623 " roo-layout-inactive-content",
39626 if (config.iframe) {
39630 style : 'border: 0px',
39631 src : 'about:blank'
39637 elcfg.html = config.html;
39641 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39642 if (config.iframe) {
39643 this.iframeEl = this.el.select('iframe',true).first();
39648 this.closable = false;
39649 this.loaded = false;
39650 this.active = false;
39653 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39655 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39657 this.wrapEl = this.el; //this.el.wrap();
39659 if (config.toolbar.items) {
39660 ti = config.toolbar.items ;
39661 delete config.toolbar.items ;
39665 this.toolbar.render(this.wrapEl, 'before');
39666 for(var i =0;i < ti.length;i++) {
39667 // Roo.log(['add child', items[i]]);
39668 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39670 this.toolbar.items = nitems;
39671 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39672 delete config.toolbar;
39676 // xtype created footer. - not sure if will work as we normally have to render first..
39677 if (this.footer && !this.footer.el && this.footer.xtype) {
39678 if (!this.wrapEl) {
39679 this.wrapEl = this.el.wrap();
39682 this.footer.container = this.wrapEl.createChild();
39684 this.footer = Roo.factory(this.footer, Roo);
39689 if(typeof config == "string"){
39690 this.title = config;
39692 Roo.apply(this, config);
39696 this.resizeEl = Roo.get(this.resizeEl, true);
39698 this.resizeEl = this.el;
39700 // handle view.xtype
39708 * Fires when this panel is activated.
39709 * @param {Roo.ContentPanel} this
39713 * @event deactivate
39714 * Fires when this panel is activated.
39715 * @param {Roo.ContentPanel} this
39717 "deactivate" : true,
39721 * Fires when this panel is resized if fitToFrame is true.
39722 * @param {Roo.ContentPanel} this
39723 * @param {Number} width The width after any component adjustments
39724 * @param {Number} height The height after any component adjustments
39730 * Fires when this tab is created
39731 * @param {Roo.ContentPanel} this
39742 if(this.autoScroll && !this.iframe){
39743 this.resizeEl.setStyle("overflow", "auto");
39745 // fix randome scrolling
39746 //this.el.on('scroll', function() {
39747 // Roo.log('fix random scolling');
39748 // this.scrollTo('top',0);
39751 content = content || this.content;
39753 this.setContent(content);
39755 if(config && config.url){
39756 this.setUrl(this.url, this.params, this.loadOnce);
39761 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39763 if (this.view && typeof(this.view.xtype) != 'undefined') {
39764 this.view.el = this.el.appendChild(document.createElement("div"));
39765 this.view = Roo.factory(this.view);
39766 this.view.render && this.view.render(false, '');
39770 this.fireEvent('render', this);
39773 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39783 setRegion : function(region){
39784 this.region = region;
39785 this.setActiveClass(region && !this.background);
39789 setActiveClass: function(state)
39792 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39793 this.el.setStyle('position','relative');
39795 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39796 this.el.setStyle('position', 'absolute');
39801 * Returns the toolbar for this Panel if one was configured.
39802 * @return {Roo.Toolbar}
39804 getToolbar : function(){
39805 return this.toolbar;
39808 setActiveState : function(active)
39810 this.active = active;
39811 this.setActiveClass(active);
39813 if(this.fireEvent("deactivate", this) === false){
39818 this.fireEvent("activate", this);
39822 * Updates this panel's element (not for iframe)
39823 * @param {String} content The new content
39824 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39826 setContent : function(content, loadScripts){
39831 this.el.update(content, loadScripts);
39834 ignoreResize : function(w, h){
39835 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39838 this.lastSize = {width: w, height: h};
39843 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39844 * @return {Roo.UpdateManager} The UpdateManager
39846 getUpdateManager : function(){
39850 return this.el.getUpdateManager();
39853 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39854 * Does not work with IFRAME contents
39855 * @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:
39858 url: "your-url.php",
39859 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39860 callback: yourFunction,
39861 scope: yourObject, //(optional scope)
39864 text: "Loading...",
39870 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39871 * 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.
39872 * @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}
39873 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39874 * @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.
39875 * @return {Roo.ContentPanel} this
39883 var um = this.el.getUpdateManager();
39884 um.update.apply(um, arguments);
39890 * 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.
39891 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39892 * @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)
39893 * @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)
39894 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39896 setUrl : function(url, params, loadOnce){
39898 this.iframeEl.dom.src = url;
39902 if(this.refreshDelegate){
39903 this.removeListener("activate", this.refreshDelegate);
39905 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39906 this.on("activate", this.refreshDelegate);
39907 return this.el.getUpdateManager();
39910 _handleRefresh : function(url, params, loadOnce){
39911 if(!loadOnce || !this.loaded){
39912 var updater = this.el.getUpdateManager();
39913 updater.update(url, params, this._setLoaded.createDelegate(this));
39917 _setLoaded : function(){
39918 this.loaded = true;
39922 * Returns this panel's id
39925 getId : function(){
39930 * Returns this panel's element - used by regiosn to add.
39931 * @return {Roo.Element}
39933 getEl : function(){
39934 return this.wrapEl || this.el;
39939 adjustForComponents : function(width, height)
39941 //Roo.log('adjustForComponents ');
39942 if(this.resizeEl != this.el){
39943 width -= this.el.getFrameWidth('lr');
39944 height -= this.el.getFrameWidth('tb');
39947 var te = this.toolbar.getEl();
39948 te.setWidth(width);
39949 height -= te.getHeight();
39952 var te = this.footer.getEl();
39953 te.setWidth(width);
39954 height -= te.getHeight();
39958 if(this.adjustments){
39959 width += this.adjustments[0];
39960 height += this.adjustments[1];
39962 return {"width": width, "height": height};
39965 setSize : function(width, height){
39966 if(this.fitToFrame && !this.ignoreResize(width, height)){
39967 if(this.fitContainer && this.resizeEl != this.el){
39968 this.el.setSize(width, height);
39970 var size = this.adjustForComponents(width, height);
39972 this.iframeEl.setSize(width,height);
39975 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39976 this.fireEvent('resize', this, size.width, size.height);
39983 * Returns this panel's title
39986 getTitle : function(){
39988 if (typeof(this.title) != 'object') {
39993 for (var k in this.title) {
39994 if (!this.title.hasOwnProperty(k)) {
39998 if (k.indexOf('-') >= 0) {
39999 var s = k.split('-');
40000 for (var i = 0; i<s.length; i++) {
40001 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40004 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40011 * Set this panel's title
40012 * @param {String} title
40014 setTitle : function(title){
40015 this.title = title;
40017 this.region.updatePanelTitle(this, title);
40022 * Returns true is this panel was configured to be closable
40023 * @return {Boolean}
40025 isClosable : function(){
40026 return this.closable;
40029 beforeSlide : function(){
40031 this.resizeEl.clip();
40034 afterSlide : function(){
40036 this.resizeEl.unclip();
40040 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40041 * Will fail silently if the {@link #setUrl} method has not been called.
40042 * This does not activate the panel, just updates its content.
40044 refresh : function(){
40045 if(this.refreshDelegate){
40046 this.loaded = false;
40047 this.refreshDelegate();
40052 * Destroys this panel
40054 destroy : function(){
40055 this.el.removeAllListeners();
40056 var tempEl = document.createElement("span");
40057 tempEl.appendChild(this.el.dom);
40058 tempEl.innerHTML = "";
40064 * form - if the content panel contains a form - this is a reference to it.
40065 * @type {Roo.form.Form}
40069 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40070 * This contains a reference to it.
40076 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40086 * @param {Object} cfg Xtype definition of item to add.
40090 getChildContainer: function () {
40091 return this.getEl();
40096 var ret = new Roo.factory(cfg);
40101 if (cfg.xtype.match(/^Form$/)) {
40104 //if (this.footer) {
40105 // el = this.footer.container.insertSibling(false, 'before');
40107 el = this.el.createChild();
40110 this.form = new Roo.form.Form(cfg);
40113 if ( this.form.allItems.length) {
40114 this.form.render(el.dom);
40118 // should only have one of theses..
40119 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40120 // views.. should not be just added - used named prop 'view''
40122 cfg.el = this.el.appendChild(document.createElement("div"));
40125 var ret = new Roo.factory(cfg);
40127 ret.render && ret.render(false, ''); // render blank..
40137 * @class Roo.bootstrap.panel.Grid
40138 * @extends Roo.bootstrap.panel.Content
40140 * Create a new GridPanel.
40141 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40142 * @param {Object} config A the config object
40148 Roo.bootstrap.panel.Grid = function(config)
40152 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40153 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40155 config.el = this.wrapper;
40156 //this.el = this.wrapper;
40158 if (config.container) {
40159 // ctor'ed from a Border/panel.grid
40162 this.wrapper.setStyle("overflow", "hidden");
40163 this.wrapper.addClass('roo-grid-container');
40168 if(config.toolbar){
40169 var tool_el = this.wrapper.createChild();
40170 this.toolbar = Roo.factory(config.toolbar);
40172 if (config.toolbar.items) {
40173 ti = config.toolbar.items ;
40174 delete config.toolbar.items ;
40178 this.toolbar.render(tool_el);
40179 for(var i =0;i < ti.length;i++) {
40180 // Roo.log(['add child', items[i]]);
40181 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40183 this.toolbar.items = nitems;
40185 delete config.toolbar;
40188 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40189 config.grid.scrollBody = true;;
40190 config.grid.monitorWindowResize = false; // turn off autosizing
40191 config.grid.autoHeight = false;
40192 config.grid.autoWidth = false;
40194 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40196 if (config.background) {
40197 // render grid on panel activation (if panel background)
40198 this.on('activate', function(gp) {
40199 if (!gp.grid.rendered) {
40200 gp.grid.render(this.wrapper);
40201 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40206 this.grid.render(this.wrapper);
40207 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40210 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40211 // ??? needed ??? config.el = this.wrapper;
40216 // xtype created footer. - not sure if will work as we normally have to render first..
40217 if (this.footer && !this.footer.el && this.footer.xtype) {
40219 var ctr = this.grid.getView().getFooterPanel(true);
40220 this.footer.dataSource = this.grid.dataSource;
40221 this.footer = Roo.factory(this.footer, Roo);
40222 this.footer.render(ctr);
40232 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40233 getId : function(){
40234 return this.grid.id;
40238 * Returns the grid for this panel
40239 * @return {Roo.bootstrap.Table}
40241 getGrid : function(){
40245 setSize : function(width, height){
40246 if(!this.ignoreResize(width, height)){
40247 var grid = this.grid;
40248 var size = this.adjustForComponents(width, height);
40249 // tfoot is not a footer?
40252 var gridel = grid.getGridEl();
40253 gridel.setSize(size.width, size.height);
40255 var tbd = grid.getGridEl().select('tbody', true).first();
40256 var thd = grid.getGridEl().select('thead',true).first();
40257 var tbf= grid.getGridEl().select('tfoot', true).first();
40260 size.height -= tbf.getHeight();
40263 size.height -= thd.getHeight();
40266 tbd.setSize(size.width, size.height );
40267 // this is for the account management tab -seems to work there.
40268 var thd = grid.getGridEl().select('thead',true).first();
40270 // tbd.setSize(size.width, size.height - thd.getHeight());
40279 beforeSlide : function(){
40280 this.grid.getView().scroller.clip();
40283 afterSlide : function(){
40284 this.grid.getView().scroller.unclip();
40287 destroy : function(){
40288 this.grid.destroy();
40290 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40295 * @class Roo.bootstrap.panel.Nest
40296 * @extends Roo.bootstrap.panel.Content
40298 * Create a new Panel, that can contain a layout.Border.
40301 * @param {Roo.BorderLayout} layout The layout for this panel
40302 * @param {String/Object} config A string to set only the title or a config object
40304 Roo.bootstrap.panel.Nest = function(config)
40306 // construct with only one argument..
40307 /* FIXME - implement nicer consturctors
40308 if (layout.layout) {
40310 layout = config.layout;
40311 delete config.layout;
40313 if (layout.xtype && !layout.getEl) {
40314 // then layout needs constructing..
40315 layout = Roo.factory(layout, Roo);
40319 config.el = config.layout.getEl();
40321 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40323 config.layout.monitorWindowResize = false; // turn off autosizing
40324 this.layout = config.layout;
40325 this.layout.getEl().addClass("roo-layout-nested-layout");
40326 this.layout.parent = this;
40333 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40335 setSize : function(width, height){
40336 if(!this.ignoreResize(width, height)){
40337 var size = this.adjustForComponents(width, height);
40338 var el = this.layout.getEl();
40339 if (size.height < 1) {
40340 el.setWidth(size.width);
40342 el.setSize(size.width, size.height);
40344 var touch = el.dom.offsetWidth;
40345 this.layout.layout();
40346 // ie requires a double layout on the first pass
40347 if(Roo.isIE && !this.initialized){
40348 this.initialized = true;
40349 this.layout.layout();
40354 // activate all subpanels if not currently active..
40356 setActiveState : function(active){
40357 this.active = active;
40358 this.setActiveClass(active);
40361 this.fireEvent("deactivate", this);
40365 this.fireEvent("activate", this);
40366 // not sure if this should happen before or after..
40367 if (!this.layout) {
40368 return; // should not happen..
40371 for (var r in this.layout.regions) {
40372 reg = this.layout.getRegion(r);
40373 if (reg.getActivePanel()) {
40374 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40375 reg.setActivePanel(reg.getActivePanel());
40378 if (!reg.panels.length) {
40381 reg.showPanel(reg.getPanel(0));
40390 * Returns the nested BorderLayout for this panel
40391 * @return {Roo.BorderLayout}
40393 getLayout : function(){
40394 return this.layout;
40398 * Adds a xtype elements to the layout of the nested panel
40402 xtype : 'ContentPanel',
40409 xtype : 'NestedLayoutPanel',
40415 items : [ ... list of content panels or nested layout panels.. ]
40419 * @param {Object} cfg Xtype definition of item to add.
40421 addxtype : function(cfg) {
40422 return this.layout.addxtype(cfg);
40427 * Ext JS Library 1.1.1
40428 * Copyright(c) 2006-2007, Ext JS, LLC.
40430 * Originally Released Under LGPL - original licence link has changed is not relivant.
40433 * <script type="text/javascript">
40436 * @class Roo.TabPanel
40437 * @extends Roo.util.Observable
40438 * A lightweight tab container.
40442 // basic tabs 1, built from existing content
40443 var tabs = new Roo.TabPanel("tabs1");
40444 tabs.addTab("script", "View Script");
40445 tabs.addTab("markup", "View Markup");
40446 tabs.activate("script");
40448 // more advanced tabs, built from javascript
40449 var jtabs = new Roo.TabPanel("jtabs");
40450 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40452 // set up the UpdateManager
40453 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40454 var updater = tab2.getUpdateManager();
40455 updater.setDefaultUrl("ajax1.htm");
40456 tab2.on('activate', updater.refresh, updater, true);
40458 // Use setUrl for Ajax loading
40459 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40460 tab3.setUrl("ajax2.htm", null, true);
40463 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40466 jtabs.activate("jtabs-1");
40469 * Create a new TabPanel.
40470 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40471 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40473 Roo.bootstrap.panel.Tabs = function(config){
40475 * The container element for this TabPanel.
40476 * @type Roo.Element
40478 this.el = Roo.get(config.el);
40481 if(typeof config == "boolean"){
40482 this.tabPosition = config ? "bottom" : "top";
40484 Roo.apply(this, config);
40488 if(this.tabPosition == "bottom"){
40489 // if tabs are at the bottom = create the body first.
40490 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40491 this.el.addClass("roo-tabs-bottom");
40493 // next create the tabs holders
40495 if (this.tabPosition == "west"){
40497 var reg = this.region; // fake it..
40499 if (!reg.mgr.parent) {
40502 reg = reg.mgr.parent.region;
40504 Roo.log("got nest?");
40506 if (reg.mgr.getRegion('west')) {
40507 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40508 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40509 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40510 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40511 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40519 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40520 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40521 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40522 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40527 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40530 // finally - if tabs are at the top, then create the body last..
40531 if(this.tabPosition != "bottom"){
40532 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40533 * @type Roo.Element
40535 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40536 this.el.addClass("roo-tabs-top");
40540 this.bodyEl.setStyle("position", "relative");
40542 this.active = null;
40543 this.activateDelegate = this.activate.createDelegate(this);
40548 * Fires when the active tab changes
40549 * @param {Roo.TabPanel} this
40550 * @param {Roo.TabPanelItem} activePanel The new active tab
40554 * @event beforetabchange
40555 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40556 * @param {Roo.TabPanel} this
40557 * @param {Object} e Set cancel to true on this object to cancel the tab change
40558 * @param {Roo.TabPanelItem} tab The tab being changed to
40560 "beforetabchange" : true
40563 Roo.EventManager.onWindowResize(this.onResize, this);
40564 this.cpad = this.el.getPadding("lr");
40565 this.hiddenCount = 0;
40568 // toolbar on the tabbar support...
40569 if (this.toolbar) {
40570 alert("no toolbar support yet");
40571 this.toolbar = false;
40573 var tcfg = this.toolbar;
40574 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40575 this.toolbar = new Roo.Toolbar(tcfg);
40576 if (Roo.isSafari) {
40577 var tbl = tcfg.container.child('table', true);
40578 tbl.setAttribute('width', '100%');
40586 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40589 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40591 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40593 tabPosition : "top",
40595 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40597 currentTabWidth : 0,
40599 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40603 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40607 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40609 preferredTabWidth : 175,
40611 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40613 resizeTabs : false,
40615 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40617 monitorResize : true,
40619 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40621 toolbar : false, // set by caller..
40623 region : false, /// set by caller
40625 disableTooltips : true, // not used yet...
40628 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40629 * @param {String} id The id of the div to use <b>or create</b>
40630 * @param {String} text The text for the tab
40631 * @param {String} content (optional) Content to put in the TabPanelItem body
40632 * @param {Boolean} closable (optional) True to create a close icon on the tab
40633 * @return {Roo.TabPanelItem} The created TabPanelItem
40635 addTab : function(id, text, content, closable, tpl)
40637 var item = new Roo.bootstrap.panel.TabItem({
40641 closable : closable,
40644 this.addTabItem(item);
40646 item.setContent(content);
40652 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40653 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40654 * @return {Roo.TabPanelItem}
40656 getTab : function(id){
40657 return this.items[id];
40661 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40662 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40664 hideTab : function(id){
40665 var t = this.items[id];
40668 this.hiddenCount++;
40669 this.autoSizeTabs();
40674 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40675 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40677 unhideTab : function(id){
40678 var t = this.items[id];
40680 t.setHidden(false);
40681 this.hiddenCount--;
40682 this.autoSizeTabs();
40687 * Adds an existing {@link Roo.TabPanelItem}.
40688 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40690 addTabItem : function(item)
40692 this.items[item.id] = item;
40693 this.items.push(item);
40694 this.autoSizeTabs();
40695 // if(this.resizeTabs){
40696 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40697 // this.autoSizeTabs();
40699 // item.autoSize();
40704 * Removes a {@link Roo.TabPanelItem}.
40705 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40707 removeTab : function(id){
40708 var items = this.items;
40709 var tab = items[id];
40710 if(!tab) { return; }
40711 var index = items.indexOf(tab);
40712 if(this.active == tab && items.length > 1){
40713 var newTab = this.getNextAvailable(index);
40718 this.stripEl.dom.removeChild(tab.pnode.dom);
40719 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40720 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40722 items.splice(index, 1);
40723 delete this.items[tab.id];
40724 tab.fireEvent("close", tab);
40725 tab.purgeListeners();
40726 this.autoSizeTabs();
40729 getNextAvailable : function(start){
40730 var items = this.items;
40732 // look for a next tab that will slide over to
40733 // replace the one being removed
40734 while(index < items.length){
40735 var item = items[++index];
40736 if(item && !item.isHidden()){
40740 // if one isn't found select the previous tab (on the left)
40743 var item = items[--index];
40744 if(item && !item.isHidden()){
40752 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40753 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40755 disableTab : function(id){
40756 var tab = this.items[id];
40757 if(tab && this.active != tab){
40763 * Enables a {@link Roo.TabPanelItem} that is disabled.
40764 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40766 enableTab : function(id){
40767 var tab = this.items[id];
40772 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40773 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40774 * @return {Roo.TabPanelItem} The TabPanelItem.
40776 activate : function(id)
40778 //Roo.log('activite:' + id);
40780 var tab = this.items[id];
40784 if(tab == this.active || tab.disabled){
40788 this.fireEvent("beforetabchange", this, e, tab);
40789 if(e.cancel !== true && !tab.disabled){
40791 this.active.hide();
40793 this.active = this.items[id];
40794 this.active.show();
40795 this.fireEvent("tabchange", this, this.active);
40801 * Gets the active {@link Roo.TabPanelItem}.
40802 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40804 getActiveTab : function(){
40805 return this.active;
40809 * Updates the tab body element to fit the height of the container element
40810 * for overflow scrolling
40811 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40813 syncHeight : function(targetHeight){
40814 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40815 var bm = this.bodyEl.getMargins();
40816 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40817 this.bodyEl.setHeight(newHeight);
40821 onResize : function(){
40822 if(this.monitorResize){
40823 this.autoSizeTabs();
40828 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40830 beginUpdate : function(){
40831 this.updating = true;
40835 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40837 endUpdate : function(){
40838 this.updating = false;
40839 this.autoSizeTabs();
40843 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40845 autoSizeTabs : function()
40847 var count = this.items.length;
40848 var vcount = count - this.hiddenCount;
40851 this.stripEl.hide();
40853 this.stripEl.show();
40856 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40861 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40862 var availWidth = Math.floor(w / vcount);
40863 var b = this.stripBody;
40864 if(b.getWidth() > w){
40865 var tabs = this.items;
40866 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40867 if(availWidth < this.minTabWidth){
40868 /*if(!this.sleft){ // incomplete scrolling code
40869 this.createScrollButtons();
40872 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40875 if(this.currentTabWidth < this.preferredTabWidth){
40876 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40882 * Returns the number of tabs in this TabPanel.
40885 getCount : function(){
40886 return this.items.length;
40890 * Resizes all the tabs to the passed width
40891 * @param {Number} The new width
40893 setTabWidth : function(width){
40894 this.currentTabWidth = width;
40895 for(var i = 0, len = this.items.length; i < len; i++) {
40896 if(!this.items[i].isHidden()) {
40897 this.items[i].setWidth(width);
40903 * Destroys this TabPanel
40904 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40906 destroy : function(removeEl){
40907 Roo.EventManager.removeResizeListener(this.onResize, this);
40908 for(var i = 0, len = this.items.length; i < len; i++){
40909 this.items[i].purgeListeners();
40911 if(removeEl === true){
40912 this.el.update("");
40917 createStrip : function(container)
40919 var strip = document.createElement("nav");
40920 strip.className = Roo.bootstrap.version == 4 ?
40921 "navbar-light bg-light" :
40922 "navbar navbar-default"; //"x-tabs-wrap";
40923 container.appendChild(strip);
40927 createStripList : function(strip)
40929 // div wrapper for retard IE
40930 // returns the "tr" element.
40931 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40932 //'<div class="x-tabs-strip-wrap">'+
40933 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40934 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40935 return strip.firstChild; //.firstChild.firstChild.firstChild;
40937 createBody : function(container)
40939 var body = document.createElement("div");
40940 Roo.id(body, "tab-body");
40941 //Roo.fly(body).addClass("x-tabs-body");
40942 Roo.fly(body).addClass("tab-content");
40943 container.appendChild(body);
40946 createItemBody :function(bodyEl, id){
40947 var body = Roo.getDom(id);
40949 body = document.createElement("div");
40952 //Roo.fly(body).addClass("x-tabs-item-body");
40953 Roo.fly(body).addClass("tab-pane");
40954 bodyEl.insertBefore(body, bodyEl.firstChild);
40958 createStripElements : function(stripEl, text, closable, tpl)
40960 var td = document.createElement("li"); // was td..
40961 td.className = 'nav-item';
40963 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40966 stripEl.appendChild(td);
40968 td.className = "x-tabs-closable";
40969 if(!this.closeTpl){
40970 this.closeTpl = new Roo.Template(
40971 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40972 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40973 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40976 var el = this.closeTpl.overwrite(td, {"text": text});
40977 var close = el.getElementsByTagName("div")[0];
40978 var inner = el.getElementsByTagName("em")[0];
40979 return {"el": el, "close": close, "inner": inner};
40982 // not sure what this is..
40983 // if(!this.tabTpl){
40984 //this.tabTpl = new Roo.Template(
40985 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40986 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40988 // this.tabTpl = new Roo.Template(
40989 // '<a href="#">' +
40990 // '<span unselectable="on"' +
40991 // (this.disableTooltips ? '' : ' title="{text}"') +
40992 // ' >{text}</span></a>'
40998 var template = tpl || this.tabTpl || false;
41001 template = new Roo.Template(
41002 Roo.bootstrap.version == 4 ?
41004 '<a class="nav-link" href="#" unselectable="on"' +
41005 (this.disableTooltips ? '' : ' title="{text}"') +
41008 '<a class="nav-link" href="#">' +
41009 '<span unselectable="on"' +
41010 (this.disableTooltips ? '' : ' title="{text}"') +
41011 ' >{text}</span></a>'
41016 switch (typeof(template)) {
41020 template = new Roo.Template(template);
41026 var el = template.overwrite(td, {"text": text});
41028 var inner = el.getElementsByTagName("span")[0];
41030 return {"el": el, "inner": inner};
41038 * @class Roo.TabPanelItem
41039 * @extends Roo.util.Observable
41040 * Represents an individual item (tab plus body) in a TabPanel.
41041 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41042 * @param {String} id The id of this TabPanelItem
41043 * @param {String} text The text for the tab of this TabPanelItem
41044 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41046 Roo.bootstrap.panel.TabItem = function(config){
41048 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41049 * @type Roo.TabPanel
41051 this.tabPanel = config.panel;
41053 * The id for this TabPanelItem
41056 this.id = config.id;
41058 this.disabled = false;
41060 this.text = config.text;
41062 this.loaded = false;
41063 this.closable = config.closable;
41066 * The body element for this TabPanelItem.
41067 * @type Roo.Element
41069 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41070 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41071 this.bodyEl.setStyle("display", "block");
41072 this.bodyEl.setStyle("zoom", "1");
41073 //this.hideAction();
41075 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41077 this.el = Roo.get(els.el);
41078 this.inner = Roo.get(els.inner, true);
41079 this.textEl = Roo.bootstrap.version == 4 ?
41080 this.el : Roo.get(this.el.dom.firstChild, true);
41082 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41083 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41086 // this.el.on("mousedown", this.onTabMouseDown, this);
41087 this.el.on("click", this.onTabClick, this);
41089 if(config.closable){
41090 var c = Roo.get(els.close, true);
41091 c.dom.title = this.closeText;
41092 c.addClassOnOver("close-over");
41093 c.on("click", this.closeClick, this);
41099 * Fires when this tab becomes the active tab.
41100 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41101 * @param {Roo.TabPanelItem} this
41105 * @event beforeclose
41106 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41107 * @param {Roo.TabPanelItem} this
41108 * @param {Object} e Set cancel to true on this object to cancel the close.
41110 "beforeclose": true,
41113 * Fires when this tab is closed.
41114 * @param {Roo.TabPanelItem} this
41118 * @event deactivate
41119 * Fires when this tab is no longer the active tab.
41120 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41121 * @param {Roo.TabPanelItem} this
41123 "deactivate" : true
41125 this.hidden = false;
41127 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41130 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41132 purgeListeners : function(){
41133 Roo.util.Observable.prototype.purgeListeners.call(this);
41134 this.el.removeAllListeners();
41137 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41140 this.status_node.addClass("active");
41143 this.tabPanel.stripWrap.repaint();
41145 this.fireEvent("activate", this.tabPanel, this);
41149 * Returns true if this tab is the active tab.
41150 * @return {Boolean}
41152 isActive : function(){
41153 return this.tabPanel.getActiveTab() == this;
41157 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41160 this.status_node.removeClass("active");
41162 this.fireEvent("deactivate", this.tabPanel, this);
41165 hideAction : function(){
41166 this.bodyEl.hide();
41167 this.bodyEl.setStyle("position", "absolute");
41168 this.bodyEl.setLeft("-20000px");
41169 this.bodyEl.setTop("-20000px");
41172 showAction : function(){
41173 this.bodyEl.setStyle("position", "relative");
41174 this.bodyEl.setTop("");
41175 this.bodyEl.setLeft("");
41176 this.bodyEl.show();
41180 * Set the tooltip for the tab.
41181 * @param {String} tooltip The tab's tooltip
41183 setTooltip : function(text){
41184 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41185 this.textEl.dom.qtip = text;
41186 this.textEl.dom.removeAttribute('title');
41188 this.textEl.dom.title = text;
41192 onTabClick : function(e){
41193 e.preventDefault();
41194 this.tabPanel.activate(this.id);
41197 onTabMouseDown : function(e){
41198 e.preventDefault();
41199 this.tabPanel.activate(this.id);
41202 getWidth : function(){
41203 return this.inner.getWidth();
41206 setWidth : function(width){
41207 var iwidth = width - this.linode.getPadding("lr");
41208 this.inner.setWidth(iwidth);
41209 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41210 this.linode.setWidth(width);
41214 * Show or hide the tab
41215 * @param {Boolean} hidden True to hide or false to show.
41217 setHidden : function(hidden){
41218 this.hidden = hidden;
41219 this.linode.setStyle("display", hidden ? "none" : "");
41223 * Returns true if this tab is "hidden"
41224 * @return {Boolean}
41226 isHidden : function(){
41227 return this.hidden;
41231 * Returns the text for this tab
41234 getText : function(){
41238 autoSize : function(){
41239 //this.el.beginMeasure();
41240 this.textEl.setWidth(1);
41242 * #2804 [new] Tabs in Roojs
41243 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41245 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41246 //this.el.endMeasure();
41250 * Sets the text for the tab (Note: this also sets the tooltip text)
41251 * @param {String} text The tab's text and tooltip
41253 setText : function(text){
41255 this.textEl.update(text);
41256 this.setTooltip(text);
41257 //if(!this.tabPanel.resizeTabs){
41258 // this.autoSize();
41262 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41264 activate : function(){
41265 this.tabPanel.activate(this.id);
41269 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41271 disable : function(){
41272 if(this.tabPanel.active != this){
41273 this.disabled = true;
41274 this.status_node.addClass("disabled");
41279 * Enables this TabPanelItem if it was previously disabled.
41281 enable : function(){
41282 this.disabled = false;
41283 this.status_node.removeClass("disabled");
41287 * Sets the content for this TabPanelItem.
41288 * @param {String} content The content
41289 * @param {Boolean} loadScripts true to look for and load scripts
41291 setContent : function(content, loadScripts){
41292 this.bodyEl.update(content, loadScripts);
41296 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41297 * @return {Roo.UpdateManager} The UpdateManager
41299 getUpdateManager : function(){
41300 return this.bodyEl.getUpdateManager();
41304 * Set a URL to be used to load the content for this TabPanelItem.
41305 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41306 * @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)
41307 * @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)
41308 * @return {Roo.UpdateManager} The UpdateManager
41310 setUrl : function(url, params, loadOnce){
41311 if(this.refreshDelegate){
41312 this.un('activate', this.refreshDelegate);
41314 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41315 this.on("activate", this.refreshDelegate);
41316 return this.bodyEl.getUpdateManager();
41320 _handleRefresh : function(url, params, loadOnce){
41321 if(!loadOnce || !this.loaded){
41322 var updater = this.bodyEl.getUpdateManager();
41323 updater.update(url, params, this._setLoaded.createDelegate(this));
41328 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41329 * Will fail silently if the setUrl method has not been called.
41330 * This does not activate the panel, just updates its content.
41332 refresh : function(){
41333 if(this.refreshDelegate){
41334 this.loaded = false;
41335 this.refreshDelegate();
41340 _setLoaded : function(){
41341 this.loaded = true;
41345 closeClick : function(e){
41348 this.fireEvent("beforeclose", this, o);
41349 if(o.cancel !== true){
41350 this.tabPanel.removeTab(this.id);
41354 * The text displayed in the tooltip for the close icon.
41357 closeText : "Close this tab"
41360 * This script refer to:
41361 * Title: International Telephone Input
41362 * Author: Jack O'Connor
41363 * Code version: v12.1.12
41364 * Availability: https://github.com/jackocnr/intl-tel-input.git
41367 Roo.bootstrap.PhoneInputData = function() {
41370 "Afghanistan (افغانستان)",
41375 "Albania (Shqipëri)",
41380 "Algeria (الجزائر)",
41405 "Antigua and Barbuda",
41415 "Armenia (Հայաստան)",
41431 "Austria (Österreich)",
41436 "Azerbaijan (Azərbaycan)",
41446 "Bahrain (البحرين)",
41451 "Bangladesh (বাংলাদেশ)",
41461 "Belarus (Беларусь)",
41466 "Belgium (België)",
41496 "Bosnia and Herzegovina (Босна и Херцеговина)",
41511 "British Indian Ocean Territory",
41516 "British Virgin Islands",
41526 "Bulgaria (България)",
41536 "Burundi (Uburundi)",
41541 "Cambodia (កម្ពុជា)",
41546 "Cameroon (Cameroun)",
41555 ["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"]
41558 "Cape Verde (Kabu Verdi)",
41563 "Caribbean Netherlands",
41574 "Central African Republic (République centrafricaine)",
41594 "Christmas Island",
41600 "Cocos (Keeling) Islands",
41611 "Comoros (جزر القمر)",
41616 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41621 "Congo (Republic) (Congo-Brazzaville)",
41641 "Croatia (Hrvatska)",
41662 "Czech Republic (Česká republika)",
41667 "Denmark (Danmark)",
41682 "Dominican Republic (República Dominicana)",
41686 ["809", "829", "849"]
41704 "Equatorial Guinea (Guinea Ecuatorial)",
41724 "Falkland Islands (Islas Malvinas)",
41729 "Faroe Islands (Føroyar)",
41750 "French Guiana (Guyane française)",
41755 "French Polynesia (Polynésie française)",
41770 "Georgia (საქართველო)",
41775 "Germany (Deutschland)",
41795 "Greenland (Kalaallit Nunaat)",
41832 "Guinea-Bissau (Guiné Bissau)",
41857 "Hungary (Magyarország)",
41862 "Iceland (Ísland)",
41882 "Iraq (العراق)",
41898 "Israel (ישראל)",
41925 "Jordan (الأردن)",
41930 "Kazakhstan (Казахстан)",
41951 "Kuwait (الكويت)",
41956 "Kyrgyzstan (Кыргызстан)",
41966 "Latvia (Latvija)",
41971 "Lebanon (لبنان)",
41986 "Libya (ليبيا)",
41996 "Lithuania (Lietuva)",
42011 "Macedonia (FYROM) (Македонија)",
42016 "Madagascar (Madagasikara)",
42046 "Marshall Islands",
42056 "Mauritania (موريتانيا)",
42061 "Mauritius (Moris)",
42082 "Moldova (Republica Moldova)",
42092 "Mongolia (Монгол)",
42097 "Montenegro (Crna Gora)",
42107 "Morocco (المغرب)",
42113 "Mozambique (Moçambique)",
42118 "Myanmar (Burma) (မြန်မာ)",
42123 "Namibia (Namibië)",
42138 "Netherlands (Nederland)",
42143 "New Caledonia (Nouvelle-Calédonie)",
42178 "North Korea (조선 민주주의 인민 공화국)",
42183 "Northern Mariana Islands",
42199 "Pakistan (پاکستان)",
42209 "Palestine (فلسطين)",
42219 "Papua New Guinea",
42261 "Réunion (La Réunion)",
42267 "Romania (România)",
42283 "Saint Barthélemy",
42294 "Saint Kitts and Nevis",
42304 "Saint Martin (Saint-Martin (partie française))",
42310 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42315 "Saint Vincent and the Grenadines",
42330 "São Tomé and Príncipe (São Tomé e Príncipe)",
42335 "Saudi Arabia (المملكة العربية السعودية)",
42340 "Senegal (Sénégal)",
42370 "Slovakia (Slovensko)",
42375 "Slovenia (Slovenija)",
42385 "Somalia (Soomaaliya)",
42395 "South Korea (대한민국)",
42400 "South Sudan (جنوب السودان)",
42410 "Sri Lanka (ශ්රී ලංකාව)",
42415 "Sudan (السودان)",
42425 "Svalbard and Jan Mayen",
42436 "Sweden (Sverige)",
42441 "Switzerland (Schweiz)",
42446 "Syria (سوريا)",
42491 "Trinidad and Tobago",
42496 "Tunisia (تونس)",
42501 "Turkey (Türkiye)",
42511 "Turks and Caicos Islands",
42521 "U.S. Virgin Islands",
42531 "Ukraine (Україна)",
42536 "United Arab Emirates (الإمارات العربية المتحدة)",
42558 "Uzbekistan (Oʻzbekiston)",
42568 "Vatican City (Città del Vaticano)",
42579 "Vietnam (Việt Nam)",
42584 "Wallis and Futuna (Wallis-et-Futuna)",
42589 "Western Sahara (الصحراء الغربية)",
42595 "Yemen (اليمن)",
42619 * This script refer to:
42620 * Title: International Telephone Input
42621 * Author: Jack O'Connor
42622 * Code version: v12.1.12
42623 * Availability: https://github.com/jackocnr/intl-tel-input.git
42627 * @class Roo.bootstrap.PhoneInput
42628 * @extends Roo.bootstrap.TriggerField
42629 * An input with International dial-code selection
42631 * @cfg {String} defaultDialCode default '+852'
42632 * @cfg {Array} preferedCountries default []
42635 * Create a new PhoneInput.
42636 * @param {Object} config Configuration options
42639 Roo.bootstrap.PhoneInput = function(config) {
42640 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42643 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42645 listWidth: undefined,
42647 selectedClass: 'active',
42649 invalidClass : "has-warning",
42651 validClass: 'has-success',
42653 allowed: '0123456789',
42658 * @cfg {String} defaultDialCode The default dial code when initializing the input
42660 defaultDialCode: '+852',
42663 * @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
42665 preferedCountries: false,
42667 getAutoCreate : function()
42669 var data = Roo.bootstrap.PhoneInputData();
42670 var align = this.labelAlign || this.parentLabelAlign();
42673 this.allCountries = [];
42674 this.dialCodeMapping = [];
42676 for (var i = 0; i < data.length; i++) {
42678 this.allCountries[i] = {
42682 priority: c[3] || 0,
42683 areaCodes: c[4] || null
42685 this.dialCodeMapping[c[2]] = {
42688 priority: c[3] || 0,
42689 areaCodes: c[4] || null
42701 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42702 maxlength: this.max_length,
42703 cls : 'form-control tel-input',
42704 autocomplete: 'new-password'
42707 var hiddenInput = {
42710 cls: 'hidden-tel-input'
42714 hiddenInput.name = this.name;
42717 if (this.disabled) {
42718 input.disabled = true;
42721 var flag_container = {
42738 cls: this.hasFeedback ? 'has-feedback' : '',
42744 cls: 'dial-code-holder',
42751 cls: 'roo-select2-container input-group',
42758 if (this.fieldLabel.length) {
42761 tooltip: 'This field is required'
42767 cls: 'control-label',
42773 html: this.fieldLabel
42776 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42782 if(this.indicatorpos == 'right') {
42783 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42790 if(align == 'left') {
42798 if(this.labelWidth > 12){
42799 label.style = "width: " + this.labelWidth + 'px';
42801 if(this.labelWidth < 13 && this.labelmd == 0){
42802 this.labelmd = this.labelWidth;
42804 if(this.labellg > 0){
42805 label.cls += ' col-lg-' + this.labellg;
42806 input.cls += ' col-lg-' + (12 - this.labellg);
42808 if(this.labelmd > 0){
42809 label.cls += ' col-md-' + this.labelmd;
42810 container.cls += ' col-md-' + (12 - this.labelmd);
42812 if(this.labelsm > 0){
42813 label.cls += ' col-sm-' + this.labelsm;
42814 container.cls += ' col-sm-' + (12 - this.labelsm);
42816 if(this.labelxs > 0){
42817 label.cls += ' col-xs-' + this.labelxs;
42818 container.cls += ' col-xs-' + (12 - this.labelxs);
42828 var settings = this;
42830 ['xs','sm','md','lg'].map(function(size){
42831 if (settings[size]) {
42832 cfg.cls += ' col-' + size + '-' + settings[size];
42836 this.store = new Roo.data.Store({
42837 proxy : new Roo.data.MemoryProxy({}),
42838 reader : new Roo.data.JsonReader({
42849 'name' : 'dialCode',
42853 'name' : 'priority',
42857 'name' : 'areaCodes',
42864 if(!this.preferedCountries) {
42865 this.preferedCountries = [
42872 var p = this.preferedCountries.reverse();
42875 for (var i = 0; i < p.length; i++) {
42876 for (var j = 0; j < this.allCountries.length; j++) {
42877 if(this.allCountries[j].iso2 == p[i]) {
42878 var t = this.allCountries[j];
42879 this.allCountries.splice(j,1);
42880 this.allCountries.unshift(t);
42886 this.store.proxy.data = {
42888 data: this.allCountries
42894 initEvents : function()
42897 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42899 this.indicator = this.indicatorEl();
42900 this.flag = this.flagEl();
42901 this.dialCodeHolder = this.dialCodeHolderEl();
42903 this.trigger = this.el.select('div.flag-box',true).first();
42904 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42909 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42910 _this.list.setWidth(lw);
42913 this.list.on('mouseover', this.onViewOver, this);
42914 this.list.on('mousemove', this.onViewMove, this);
42915 this.inputEl().on("keyup", this.onKeyUp, this);
42916 this.inputEl().on("keypress", this.onKeyPress, this);
42918 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42920 this.view = new Roo.View(this.list, this.tpl, {
42921 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42924 this.view.on('click', this.onViewClick, this);
42925 this.setValue(this.defaultDialCode);
42928 onTriggerClick : function(e)
42930 Roo.log('trigger click');
42935 if(this.isExpanded()){
42937 this.hasFocus = false;
42939 this.store.load({});
42940 this.hasFocus = true;
42945 isExpanded : function()
42947 return this.list.isVisible();
42950 collapse : function()
42952 if(!this.isExpanded()){
42956 Roo.get(document).un('mousedown', this.collapseIf, this);
42957 Roo.get(document).un('mousewheel', this.collapseIf, this);
42958 this.fireEvent('collapse', this);
42962 expand : function()
42966 if(this.isExpanded() || !this.hasFocus){
42970 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42971 this.list.setWidth(lw);
42974 this.restrictHeight();
42976 Roo.get(document).on('mousedown', this.collapseIf, this);
42977 Roo.get(document).on('mousewheel', this.collapseIf, this);
42979 this.fireEvent('expand', this);
42982 restrictHeight : function()
42984 this.list.alignTo(this.inputEl(), this.listAlign);
42985 this.list.alignTo(this.inputEl(), this.listAlign);
42988 onViewOver : function(e, t)
42990 if(this.inKeyMode){
42993 var item = this.view.findItemFromChild(t);
42996 var index = this.view.indexOf(item);
42997 this.select(index, false);
43002 onViewClick : function(view, doFocus, el, e)
43004 var index = this.view.getSelectedIndexes()[0];
43006 var r = this.store.getAt(index);
43009 this.onSelect(r, index);
43011 if(doFocus !== false && !this.blockFocus){
43012 this.inputEl().focus();
43016 onViewMove : function(e, t)
43018 this.inKeyMode = false;
43021 select : function(index, scrollIntoView)
43023 this.selectedIndex = index;
43024 this.view.select(index);
43025 if(scrollIntoView !== false){
43026 var el = this.view.getNode(index);
43028 this.list.scrollChildIntoView(el, false);
43033 createList : function()
43035 this.list = Roo.get(document.body).createChild({
43037 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43038 style: 'display:none'
43041 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43044 collapseIf : function(e)
43046 var in_combo = e.within(this.el);
43047 var in_list = e.within(this.list);
43048 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43050 if (in_combo || in_list || is_list) {
43056 onSelect : function(record, index)
43058 if(this.fireEvent('beforeselect', this, record, index) !== false){
43060 this.setFlagClass(record.data.iso2);
43061 this.setDialCode(record.data.dialCode);
43062 this.hasFocus = false;
43064 this.fireEvent('select', this, record, index);
43068 flagEl : function()
43070 var flag = this.el.select('div.flag',true).first();
43077 dialCodeHolderEl : function()
43079 var d = this.el.select('input.dial-code-holder',true).first();
43086 setDialCode : function(v)
43088 this.dialCodeHolder.dom.value = '+'+v;
43091 setFlagClass : function(n)
43093 this.flag.dom.className = 'flag '+n;
43096 getValue : function()
43098 var v = this.inputEl().getValue();
43099 if(this.dialCodeHolder) {
43100 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43105 setValue : function(v)
43107 var d = this.getDialCode(v);
43109 //invalid dial code
43110 if(v.length == 0 || !d || d.length == 0) {
43112 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43113 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43119 this.setFlagClass(this.dialCodeMapping[d].iso2);
43120 this.setDialCode(d);
43121 this.inputEl().dom.value = v.replace('+'+d,'');
43122 this.hiddenEl().dom.value = this.getValue();
43127 getDialCode : function(v)
43131 if (v.length == 0) {
43132 return this.dialCodeHolder.dom.value;
43136 if (v.charAt(0) != "+") {
43139 var numericChars = "";
43140 for (var i = 1; i < v.length; i++) {
43141 var c = v.charAt(i);
43144 if (this.dialCodeMapping[numericChars]) {
43145 dialCode = v.substr(1, i);
43147 if (numericChars.length == 4) {
43157 this.setValue(this.defaultDialCode);
43161 hiddenEl : function()
43163 return this.el.select('input.hidden-tel-input',true).first();
43166 // after setting val
43167 onKeyUp : function(e){
43168 this.setValue(this.getValue());
43171 onKeyPress : function(e){
43172 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43179 * @class Roo.bootstrap.MoneyField
43180 * @extends Roo.bootstrap.ComboBox
43181 * Bootstrap MoneyField class
43184 * Create a new MoneyField.
43185 * @param {Object} config Configuration options
43188 Roo.bootstrap.MoneyField = function(config) {
43190 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43194 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43197 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43199 allowDecimals : true,
43201 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43203 decimalSeparator : ".",
43205 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43207 decimalPrecision : 0,
43209 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43211 allowNegative : true,
43213 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43217 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43219 minValue : Number.NEGATIVE_INFINITY,
43221 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43223 maxValue : Number.MAX_VALUE,
43225 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43227 minText : "The minimum value for this field is {0}",
43229 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43231 maxText : "The maximum value for this field is {0}",
43233 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43234 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43236 nanText : "{0} is not a valid number",
43238 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43242 * @cfg {String} defaults currency of the MoneyField
43243 * value should be in lkey
43245 defaultCurrency : false,
43247 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43249 thousandsDelimiter : false,
43251 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43262 getAutoCreate : function()
43264 var align = this.labelAlign || this.parentLabelAlign();
43276 cls : 'form-control roo-money-amount-input',
43277 autocomplete: 'new-password'
43280 var hiddenInput = {
43284 cls: 'hidden-number-input'
43287 if(this.max_length) {
43288 input.maxlength = this.max_length;
43292 hiddenInput.name = this.name;
43295 if (this.disabled) {
43296 input.disabled = true;
43299 var clg = 12 - this.inputlg;
43300 var cmd = 12 - this.inputmd;
43301 var csm = 12 - this.inputsm;
43302 var cxs = 12 - this.inputxs;
43306 cls : 'row roo-money-field',
43310 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43314 cls: 'roo-select2-container input-group',
43318 cls : 'form-control roo-money-currency-input',
43319 autocomplete: 'new-password',
43321 name : this.currencyName
43325 cls : 'input-group-addon',
43339 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43343 cls: this.hasFeedback ? 'has-feedback' : '',
43354 if (this.fieldLabel.length) {
43357 tooltip: 'This field is required'
43363 cls: 'control-label',
43369 html: this.fieldLabel
43372 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43378 if(this.indicatorpos == 'right') {
43379 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43386 if(align == 'left') {
43394 if(this.labelWidth > 12){
43395 label.style = "width: " + this.labelWidth + 'px';
43397 if(this.labelWidth < 13 && this.labelmd == 0){
43398 this.labelmd = this.labelWidth;
43400 if(this.labellg > 0){
43401 label.cls += ' col-lg-' + this.labellg;
43402 input.cls += ' col-lg-' + (12 - this.labellg);
43404 if(this.labelmd > 0){
43405 label.cls += ' col-md-' + this.labelmd;
43406 container.cls += ' col-md-' + (12 - this.labelmd);
43408 if(this.labelsm > 0){
43409 label.cls += ' col-sm-' + this.labelsm;
43410 container.cls += ' col-sm-' + (12 - this.labelsm);
43412 if(this.labelxs > 0){
43413 label.cls += ' col-xs-' + this.labelxs;
43414 container.cls += ' col-xs-' + (12 - this.labelxs);
43425 var settings = this;
43427 ['xs','sm','md','lg'].map(function(size){
43428 if (settings[size]) {
43429 cfg.cls += ' col-' + size + '-' + settings[size];
43436 initEvents : function()
43438 this.indicator = this.indicatorEl();
43440 this.initCurrencyEvent();
43442 this.initNumberEvent();
43445 initCurrencyEvent : function()
43448 throw "can not find store for combo";
43451 this.store = Roo.factory(this.store, Roo.data);
43452 this.store.parent = this;
43456 this.triggerEl = this.el.select('.input-group-addon', true).first();
43458 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43463 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43464 _this.list.setWidth(lw);
43467 this.list.on('mouseover', this.onViewOver, this);
43468 this.list.on('mousemove', this.onViewMove, this);
43469 this.list.on('scroll', this.onViewScroll, this);
43472 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43475 this.view = new Roo.View(this.list, this.tpl, {
43476 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43479 this.view.on('click', this.onViewClick, this);
43481 this.store.on('beforeload', this.onBeforeLoad, this);
43482 this.store.on('load', this.onLoad, this);
43483 this.store.on('loadexception', this.onLoadException, this);
43485 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43486 "up" : function(e){
43487 this.inKeyMode = true;
43491 "down" : function(e){
43492 if(!this.isExpanded()){
43493 this.onTriggerClick();
43495 this.inKeyMode = true;
43500 "enter" : function(e){
43503 if(this.fireEvent("specialkey", this, e)){
43504 this.onViewClick(false);
43510 "esc" : function(e){
43514 "tab" : function(e){
43517 if(this.fireEvent("specialkey", this, e)){
43518 this.onViewClick(false);
43526 doRelay : function(foo, bar, hname){
43527 if(hname == 'down' || this.scope.isExpanded()){
43528 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43536 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43540 initNumberEvent : function(e)
43542 this.inputEl().on("keydown" , this.fireKey, this);
43543 this.inputEl().on("focus", this.onFocus, this);
43544 this.inputEl().on("blur", this.onBlur, this);
43546 this.inputEl().relayEvent('keyup', this);
43548 if(this.indicator){
43549 this.indicator.addClass('invisible');
43552 this.originalValue = this.getValue();
43554 if(this.validationEvent == 'keyup'){
43555 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43556 this.inputEl().on('keyup', this.filterValidation, this);
43558 else if(this.validationEvent !== false){
43559 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43562 if(this.selectOnFocus){
43563 this.on("focus", this.preFocus, this);
43566 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43567 this.inputEl().on("keypress", this.filterKeys, this);
43569 this.inputEl().relayEvent('keypress', this);
43572 var allowed = "0123456789";
43574 if(this.allowDecimals){
43575 allowed += this.decimalSeparator;
43578 if(this.allowNegative){
43582 if(this.thousandsDelimiter) {
43586 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43588 var keyPress = function(e){
43590 var k = e.getKey();
43592 var c = e.getCharCode();
43595 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43596 allowed.indexOf(String.fromCharCode(c)) === -1
43602 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43606 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43611 this.inputEl().on("keypress", keyPress, this);
43615 onTriggerClick : function(e)
43622 this.loadNext = false;
43624 if(this.isExpanded()){
43629 this.hasFocus = true;
43631 if(this.triggerAction == 'all') {
43632 this.doQuery(this.allQuery, true);
43636 this.doQuery(this.getRawValue());
43639 getCurrency : function()
43641 var v = this.currencyEl().getValue();
43646 restrictHeight : function()
43648 this.list.alignTo(this.currencyEl(), this.listAlign);
43649 this.list.alignTo(this.currencyEl(), this.listAlign);
43652 onViewClick : function(view, doFocus, el, e)
43654 var index = this.view.getSelectedIndexes()[0];
43656 var r = this.store.getAt(index);
43659 this.onSelect(r, index);
43663 onSelect : function(record, index){
43665 if(this.fireEvent('beforeselect', this, record, index) !== false){
43667 this.setFromCurrencyData(index > -1 ? record.data : false);
43671 this.fireEvent('select', this, record, index);
43675 setFromCurrencyData : function(o)
43679 this.lastCurrency = o;
43681 if (this.currencyField) {
43682 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43684 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43687 this.lastSelectionText = currency;
43689 //setting default currency
43690 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43691 this.setCurrency(this.defaultCurrency);
43695 this.setCurrency(currency);
43698 setFromData : function(o)
43702 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43704 this.setFromCurrencyData(c);
43709 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43711 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43714 this.setValue(value);
43718 setCurrency : function(v)
43720 this.currencyValue = v;
43723 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43728 setValue : function(v)
43730 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43736 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43738 this.inputEl().dom.value = (v == '') ? '' :
43739 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43741 if(!this.allowZero && v === '0') {
43742 this.hiddenEl().dom.value = '';
43743 this.inputEl().dom.value = '';
43750 getRawValue : function()
43752 var v = this.inputEl().getValue();
43757 getValue : function()
43759 return this.fixPrecision(this.parseValue(this.getRawValue()));
43762 parseValue : function(value)
43764 if(this.thousandsDelimiter) {
43766 r = new RegExp(",", "g");
43767 value = value.replace(r, "");
43770 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43771 return isNaN(value) ? '' : value;
43775 fixPrecision : function(value)
43777 if(this.thousandsDelimiter) {
43779 r = new RegExp(",", "g");
43780 value = value.replace(r, "");
43783 var nan = isNaN(value);
43785 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43786 return nan ? '' : value;
43788 return parseFloat(value).toFixed(this.decimalPrecision);
43791 decimalPrecisionFcn : function(v)
43793 return Math.floor(v);
43796 validateValue : function(value)
43798 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43802 var num = this.parseValue(value);
43805 this.markInvalid(String.format(this.nanText, value));
43809 if(num < this.minValue){
43810 this.markInvalid(String.format(this.minText, this.minValue));
43814 if(num > this.maxValue){
43815 this.markInvalid(String.format(this.maxText, this.maxValue));
43822 validate : function()
43824 if(this.disabled || this.allowBlank){
43829 var currency = this.getCurrency();
43831 if(this.validateValue(this.getRawValue()) && currency.length){
43836 this.markInvalid();
43840 getName: function()
43845 beforeBlur : function()
43851 var v = this.parseValue(this.getRawValue());
43858 onBlur : function()
43862 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43863 //this.el.removeClass(this.focusClass);
43866 this.hasFocus = false;
43868 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43872 var v = this.getValue();
43874 if(String(v) !== String(this.startValue)){
43875 this.fireEvent('change', this, v, this.startValue);
43878 this.fireEvent("blur", this);
43881 inputEl : function()
43883 return this.el.select('.roo-money-amount-input', true).first();
43886 currencyEl : function()
43888 return this.el.select('.roo-money-currency-input', true).first();
43891 hiddenEl : function()
43893 return this.el.select('input.hidden-number-input',true).first();
43897 * @class Roo.bootstrap.BezierSignature
43898 * @extends Roo.bootstrap.Component
43899 * Bootstrap BezierSignature class
43900 * This script refer to:
43901 * Title: Signature Pad
43903 * Availability: https://github.com/szimek/signature_pad
43906 * Create a new BezierSignature
43907 * @param {Object} config The config object
43910 Roo.bootstrap.BezierSignature = function(config){
43911 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43917 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43924 mouse_btn_down: true,
43927 * @cfg {int} canvas height
43929 canvas_height: '200px',
43932 * @cfg {float|function} Radius of a single dot.
43937 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43942 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43947 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43952 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43957 * @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.
43959 bg_color: 'rgba(0, 0, 0, 0)',
43962 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43964 dot_color: 'black',
43967 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43969 velocity_filter_weight: 0.7,
43972 * @cfg {function} Callback when stroke begin.
43977 * @cfg {function} Callback when stroke end.
43981 getAutoCreate : function()
43983 var cls = 'roo-signature column';
43986 cls += ' ' + this.cls;
43996 for(var i = 0; i < col_sizes.length; i++) {
43997 if(this[col_sizes[i]]) {
43998 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44008 cls: 'roo-signature-body',
44012 cls: 'roo-signature-body-canvas',
44013 height: this.canvas_height,
44014 width: this.canvas_width
44021 style: 'display: none'
44029 initEvents: function()
44031 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44033 var canvas = this.canvasEl();
44035 // mouse && touch event swapping...
44036 canvas.dom.style.touchAction = 'none';
44037 canvas.dom.style.msTouchAction = 'none';
44039 this.mouse_btn_down = false;
44040 canvas.on('mousedown', this._handleMouseDown, this);
44041 canvas.on('mousemove', this._handleMouseMove, this);
44042 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44044 if (window.PointerEvent) {
44045 canvas.on('pointerdown', this._handleMouseDown, this);
44046 canvas.on('pointermove', this._handleMouseMove, this);
44047 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44050 if ('ontouchstart' in window) {
44051 canvas.on('touchstart', this._handleTouchStart, this);
44052 canvas.on('touchmove', this._handleTouchMove, this);
44053 canvas.on('touchend', this._handleTouchEnd, this);
44056 Roo.EventManager.onWindowResize(this.resize, this, true);
44058 // file input event
44059 this.fileEl().on('change', this.uploadImage, this);
44066 resize: function(){
44068 var canvas = this.canvasEl().dom;
44069 var ctx = this.canvasElCtx();
44070 var img_data = false;
44072 if(canvas.width > 0) {
44073 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44075 // setting canvas width will clean img data
44078 var style = window.getComputedStyle ?
44079 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44081 var padding_left = parseInt(style.paddingLeft) || 0;
44082 var padding_right = parseInt(style.paddingRight) || 0;
44084 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44087 ctx.putImageData(img_data, 0, 0);
44091 _handleMouseDown: function(e)
44093 if (e.browserEvent.which === 1) {
44094 this.mouse_btn_down = true;
44095 this.strokeBegin(e);
44099 _handleMouseMove: function (e)
44101 if (this.mouse_btn_down) {
44102 this.strokeMoveUpdate(e);
44106 _handleMouseUp: function (e)
44108 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44109 this.mouse_btn_down = false;
44114 _handleTouchStart: function (e) {
44116 e.preventDefault();
44117 if (e.browserEvent.targetTouches.length === 1) {
44118 // var touch = e.browserEvent.changedTouches[0];
44119 // this.strokeBegin(touch);
44121 this.strokeBegin(e); // assume e catching the correct xy...
44125 _handleTouchMove: function (e) {
44126 e.preventDefault();
44127 // var touch = event.targetTouches[0];
44128 // _this._strokeMoveUpdate(touch);
44129 this.strokeMoveUpdate(e);
44132 _handleTouchEnd: function (e) {
44133 var wasCanvasTouched = e.target === this.canvasEl().dom;
44134 if (wasCanvasTouched) {
44135 e.preventDefault();
44136 // var touch = event.changedTouches[0];
44137 // _this._strokeEnd(touch);
44142 reset: function () {
44143 this._lastPoints = [];
44144 this._lastVelocity = 0;
44145 this._lastWidth = (this.min_width + this.max_width) / 2;
44146 this.canvasElCtx().fillStyle = this.dot_color;
44149 strokeMoveUpdate: function(e)
44151 this.strokeUpdate(e);
44153 if (this.throttle) {
44154 this.throttleStroke(this.strokeUpdate, this.throttle);
44157 this.strokeUpdate(e);
44161 strokeBegin: function(e)
44163 var newPointGroup = {
44164 color: this.dot_color,
44168 if (typeof this.onBegin === 'function') {
44172 this.curve_data.push(newPointGroup);
44174 this.strokeUpdate(e);
44177 strokeUpdate: function(e)
44179 var rect = this.canvasEl().dom.getBoundingClientRect();
44180 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44181 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44182 var lastPoints = lastPointGroup.points;
44183 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44184 var isLastPointTooClose = lastPoint
44185 ? point.distanceTo(lastPoint) <= this.min_distance
44187 var color = lastPointGroup.color;
44188 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44189 var curve = this.addPoint(point);
44191 this.drawDot({color: color, point: point});
44194 this.drawCurve({color: color, curve: curve});
44204 strokeEnd: function(e)
44206 this.strokeUpdate(e);
44207 if (typeof this.onEnd === 'function') {
44212 addPoint: function (point) {
44213 var _lastPoints = this._lastPoints;
44214 _lastPoints.push(point);
44215 if (_lastPoints.length > 2) {
44216 if (_lastPoints.length === 3) {
44217 _lastPoints.unshift(_lastPoints[0]);
44219 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44220 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44221 _lastPoints.shift();
44227 calculateCurveWidths: function (startPoint, endPoint) {
44228 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44229 (1 - this.velocity_filter_weight) * this._lastVelocity;
44231 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44234 start: this._lastWidth
44237 this._lastVelocity = velocity;
44238 this._lastWidth = newWidth;
44242 drawDot: function (_a) {
44243 var color = _a.color, point = _a.point;
44244 var ctx = this.canvasElCtx();
44245 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44247 this.drawCurveSegment(point.x, point.y, width);
44249 ctx.fillStyle = color;
44253 drawCurve: function (_a) {
44254 var color = _a.color, curve = _a.curve;
44255 var ctx = this.canvasElCtx();
44256 var widthDelta = curve.endWidth - curve.startWidth;
44257 var drawSteps = Math.floor(curve.length()) * 2;
44259 ctx.fillStyle = color;
44260 for (var i = 0; i < drawSteps; i += 1) {
44261 var t = i / drawSteps;
44267 var x = uuu * curve.startPoint.x;
44268 x += 3 * uu * t * curve.control1.x;
44269 x += 3 * u * tt * curve.control2.x;
44270 x += ttt * curve.endPoint.x;
44271 var y = uuu * curve.startPoint.y;
44272 y += 3 * uu * t * curve.control1.y;
44273 y += 3 * u * tt * curve.control2.y;
44274 y += ttt * curve.endPoint.y;
44275 var width = curve.startWidth + ttt * widthDelta;
44276 this.drawCurveSegment(x, y, width);
44282 drawCurveSegment: function (x, y, width) {
44283 var ctx = this.canvasElCtx();
44285 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44286 this.is_empty = false;
44291 var ctx = this.canvasElCtx();
44292 var canvas = this.canvasEl().dom;
44293 ctx.fillStyle = this.bg_color;
44294 ctx.clearRect(0, 0, canvas.width, canvas.height);
44295 ctx.fillRect(0, 0, canvas.width, canvas.height);
44296 this.curve_data = [];
44298 this.is_empty = true;
44303 return this.el.select('input',true).first();
44306 canvasEl: function()
44308 return this.el.select('canvas',true).first();
44311 canvasElCtx: function()
44313 return this.el.select('canvas',true).first().dom.getContext('2d');
44316 getImage: function(type)
44318 if(this.is_empty) {
44323 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44326 drawFromImage: function(img_src)
44328 var img = new Image();
44330 img.onload = function(){
44331 this.canvasElCtx().drawImage(img, 0, 0);
44336 this.is_empty = false;
44339 selectImage: function()
44341 this.fileEl().dom.click();
44344 uploadImage: function(e)
44346 var reader = new FileReader();
44348 reader.onload = function(e){
44349 var img = new Image();
44350 img.onload = function(){
44352 this.canvasElCtx().drawImage(img, 0, 0);
44354 img.src = e.target.result;
44357 reader.readAsDataURL(e.target.files[0]);
44360 // Bezier Point Constructor
44361 Point: (function () {
44362 function Point(x, y, time) {
44365 this.time = time || Date.now();
44367 Point.prototype.distanceTo = function (start) {
44368 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44370 Point.prototype.equals = function (other) {
44371 return this.x === other.x && this.y === other.y && this.time === other.time;
44373 Point.prototype.velocityFrom = function (start) {
44374 return this.time !== start.time
44375 ? this.distanceTo(start) / (this.time - start.time)
44382 // Bezier Constructor
44383 Bezier: (function () {
44384 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44385 this.startPoint = startPoint;
44386 this.control2 = control2;
44387 this.control1 = control1;
44388 this.endPoint = endPoint;
44389 this.startWidth = startWidth;
44390 this.endWidth = endWidth;
44392 Bezier.fromPoints = function (points, widths, scope) {
44393 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44394 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44395 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44397 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44398 var dx1 = s1.x - s2.x;
44399 var dy1 = s1.y - s2.y;
44400 var dx2 = s2.x - s3.x;
44401 var dy2 = s2.y - s3.y;
44402 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44403 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44404 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44405 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44406 var dxm = m1.x - m2.x;
44407 var dym = m1.y - m2.y;
44408 var k = l2 / (l1 + l2);
44409 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44410 var tx = s2.x - cm.x;
44411 var ty = s2.y - cm.y;
44413 c1: new scope.Point(m1.x + tx, m1.y + ty),
44414 c2: new scope.Point(m2.x + tx, m2.y + ty)
44417 Bezier.prototype.length = function () {
44422 for (var i = 0; i <= steps; i += 1) {
44424 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44425 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44427 var xdiff = cx - px;
44428 var ydiff = cy - py;
44429 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44436 Bezier.prototype.point = function (t, start, c1, c2, end) {
44437 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44438 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44439 + (3.0 * c2 * (1.0 - t) * t * t)
44440 + (end * t * t * t);
44445 throttleStroke: function(fn, wait) {
44446 if (wait === void 0) { wait = 250; }
44448 var timeout = null;
44452 var later = function () {
44453 previous = Date.now();
44455 result = fn.apply(storedContext, storedArgs);
44457 storedContext = null;
44461 return function wrapper() {
44463 for (var _i = 0; _i < arguments.length; _i++) {
44464 args[_i] = arguments[_i];
44466 var now = Date.now();
44467 var remaining = wait - (now - previous);
44468 storedContext = this;
44470 if (remaining <= 0 || remaining > wait) {
44472 clearTimeout(timeout);
44476 result = fn.apply(storedContext, storedArgs);
44478 storedContext = null;
44482 else if (!timeout) {
44483 timeout = window.setTimeout(later, remaining);