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.addEventListener("load", function() {
12760 data.srcdata = reader.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()
12792 this.fileCollection.forEach(function(e) {
12796 this.inputEl().dom.value = JSON.stringify(data);
12806 Roo.bootstrap.CardUploader.ID = -1;/*
12808 * Ext JS Library 1.1.1
12809 * Copyright(c) 2006-2007, Ext JS, LLC.
12811 * Originally Released Under LGPL - original licence link has changed is not relivant.
12814 * <script type="text/javascript">
12819 * @class Roo.data.SortTypes
12821 * Defines the default sorting (casting?) comparison functions used when sorting data.
12823 Roo.data.SortTypes = {
12825 * Default sort that does nothing
12826 * @param {Mixed} s The value being converted
12827 * @return {Mixed} The comparison value
12829 none : function(s){
12834 * The regular expression used to strip tags
12838 stripTagsRE : /<\/?[^>]+>/gi,
12841 * Strips all HTML tags to sort on text only
12842 * @param {Mixed} s The value being converted
12843 * @return {String} The comparison value
12845 asText : function(s){
12846 return String(s).replace(this.stripTagsRE, "");
12850 * Strips all HTML tags to sort on text only - Case insensitive
12851 * @param {Mixed} s The value being converted
12852 * @return {String} The comparison value
12854 asUCText : function(s){
12855 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12859 * Case insensitive string
12860 * @param {Mixed} s The value being converted
12861 * @return {String} The comparison value
12863 asUCString : function(s) {
12864 return String(s).toUpperCase();
12869 * @param {Mixed} s The value being converted
12870 * @return {Number} The comparison value
12872 asDate : function(s) {
12876 if(s instanceof Date){
12877 return s.getTime();
12879 return Date.parse(String(s));
12884 * @param {Mixed} s The value being converted
12885 * @return {Float} The comparison value
12887 asFloat : function(s) {
12888 var val = parseFloat(String(s).replace(/,/g, ""));
12897 * @param {Mixed} s The value being converted
12898 * @return {Number} The comparison value
12900 asInt : function(s) {
12901 var val = parseInt(String(s).replace(/,/g, ""));
12909 * Ext JS Library 1.1.1
12910 * Copyright(c) 2006-2007, Ext JS, LLC.
12912 * Originally Released Under LGPL - original licence link has changed is not relivant.
12915 * <script type="text/javascript">
12919 * @class Roo.data.Record
12920 * Instances of this class encapsulate both record <em>definition</em> information, and record
12921 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12922 * to access Records cached in an {@link Roo.data.Store} object.<br>
12924 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12925 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12928 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12930 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12931 * {@link #create}. The parameters are the same.
12932 * @param {Array} data An associative Array of data values keyed by the field name.
12933 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12934 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12935 * not specified an integer id is generated.
12937 Roo.data.Record = function(data, id){
12938 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12943 * Generate a constructor for a specific record layout.
12944 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12945 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12946 * Each field definition object may contain the following properties: <ul>
12947 * <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,
12948 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12949 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12950 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12951 * is being used, then this is a string containing the javascript expression to reference the data relative to
12952 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12953 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12954 * this may be omitted.</p></li>
12955 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12956 * <ul><li>auto (Default, implies no conversion)</li>
12961 * <li>date</li></ul></p></li>
12962 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12963 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12964 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12965 * by the Reader into an object that will be stored in the Record. It is passed the
12966 * following parameters:<ul>
12967 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12969 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12971 * <br>usage:<br><pre><code>
12972 var TopicRecord = Roo.data.Record.create(
12973 {name: 'title', mapping: 'topic_title'},
12974 {name: 'author', mapping: 'username'},
12975 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12976 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12977 {name: 'lastPoster', mapping: 'user2'},
12978 {name: 'excerpt', mapping: 'post_text'}
12981 var myNewRecord = new TopicRecord({
12982 title: 'Do my job please',
12985 lastPost: new Date(),
12986 lastPoster: 'Animal',
12987 excerpt: 'No way dude!'
12989 myStore.add(myNewRecord);
12994 Roo.data.Record.create = function(o){
12995 var f = function(){
12996 f.superclass.constructor.apply(this, arguments);
12998 Roo.extend(f, Roo.data.Record);
12999 var p = f.prototype;
13000 p.fields = new Roo.util.MixedCollection(false, function(field){
13003 for(var i = 0, len = o.length; i < len; i++){
13004 p.fields.add(new Roo.data.Field(o[i]));
13006 f.getField = function(name){
13007 return p.fields.get(name);
13012 Roo.data.Record.AUTO_ID = 1000;
13013 Roo.data.Record.EDIT = 'edit';
13014 Roo.data.Record.REJECT = 'reject';
13015 Roo.data.Record.COMMIT = 'commit';
13017 Roo.data.Record.prototype = {
13019 * Readonly flag - true if this record has been modified.
13028 join : function(store){
13029 this.store = store;
13033 * Set the named field to the specified value.
13034 * @param {String} name The name of the field to set.
13035 * @param {Object} value The value to set the field to.
13037 set : function(name, value){
13038 if(this.data[name] == value){
13042 if(!this.modified){
13043 this.modified = {};
13045 if(typeof this.modified[name] == 'undefined'){
13046 this.modified[name] = this.data[name];
13048 this.data[name] = value;
13049 if(!this.editing && this.store){
13050 this.store.afterEdit(this);
13055 * Get the value of the named field.
13056 * @param {String} name The name of the field to get the value of.
13057 * @return {Object} The value of the field.
13059 get : function(name){
13060 return this.data[name];
13064 beginEdit : function(){
13065 this.editing = true;
13066 this.modified = {};
13070 cancelEdit : function(){
13071 this.editing = false;
13072 delete this.modified;
13076 endEdit : function(){
13077 this.editing = false;
13078 if(this.dirty && this.store){
13079 this.store.afterEdit(this);
13084 * Usually called by the {@link Roo.data.Store} which owns the Record.
13085 * Rejects all changes made to the Record since either creation, or the last commit operation.
13086 * Modified fields are reverted to their original values.
13088 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13089 * of reject operations.
13091 reject : function(){
13092 var m = this.modified;
13094 if(typeof m[n] != "function"){
13095 this.data[n] = m[n];
13098 this.dirty = false;
13099 delete this.modified;
13100 this.editing = false;
13102 this.store.afterReject(this);
13107 * Usually called by the {@link Roo.data.Store} which owns the Record.
13108 * Commits all changes made to the Record since either creation, or the last commit operation.
13110 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13111 * of commit operations.
13113 commit : function(){
13114 this.dirty = false;
13115 delete this.modified;
13116 this.editing = false;
13118 this.store.afterCommit(this);
13123 hasError : function(){
13124 return this.error != null;
13128 clearError : function(){
13133 * Creates a copy of this record.
13134 * @param {String} id (optional) A new record id if you don't want to use this record's id
13137 copy : function(newId) {
13138 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13142 * Ext JS Library 1.1.1
13143 * Copyright(c) 2006-2007, Ext JS, LLC.
13145 * Originally Released Under LGPL - original licence link has changed is not relivant.
13148 * <script type="text/javascript">
13154 * @class Roo.data.Store
13155 * @extends Roo.util.Observable
13156 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13157 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13159 * 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
13160 * has no knowledge of the format of the data returned by the Proxy.<br>
13162 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13163 * instances from the data object. These records are cached and made available through accessor functions.
13165 * Creates a new Store.
13166 * @param {Object} config A config object containing the objects needed for the Store to access data,
13167 * and read the data into Records.
13169 Roo.data.Store = function(config){
13170 this.data = new Roo.util.MixedCollection(false);
13171 this.data.getKey = function(o){
13174 this.baseParams = {};
13176 this.paramNames = {
13181 "multisort" : "_multisort"
13184 if(config && config.data){
13185 this.inlineData = config.data;
13186 delete config.data;
13189 Roo.apply(this, config);
13191 if(this.reader){ // reader passed
13192 this.reader = Roo.factory(this.reader, Roo.data);
13193 this.reader.xmodule = this.xmodule || false;
13194 if(!this.recordType){
13195 this.recordType = this.reader.recordType;
13197 if(this.reader.onMetaChange){
13198 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13202 if(this.recordType){
13203 this.fields = this.recordType.prototype.fields;
13205 this.modified = [];
13209 * @event datachanged
13210 * Fires when the data cache has changed, and a widget which is using this Store
13211 * as a Record cache should refresh its view.
13212 * @param {Store} this
13214 datachanged : true,
13216 * @event metachange
13217 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13218 * @param {Store} this
13219 * @param {Object} meta The JSON metadata
13224 * Fires when Records have been added to the Store
13225 * @param {Store} this
13226 * @param {Roo.data.Record[]} records The array of Records added
13227 * @param {Number} index The index at which the record(s) were added
13232 * Fires when a Record has been removed from the Store
13233 * @param {Store} this
13234 * @param {Roo.data.Record} record The Record that was removed
13235 * @param {Number} index The index at which the record was removed
13240 * Fires when a Record has been updated
13241 * @param {Store} this
13242 * @param {Roo.data.Record} record The Record that was updated
13243 * @param {String} operation The update operation being performed. Value may be one of:
13245 Roo.data.Record.EDIT
13246 Roo.data.Record.REJECT
13247 Roo.data.Record.COMMIT
13253 * Fires when the data cache has been cleared.
13254 * @param {Store} this
13258 * @event beforeload
13259 * Fires before a request is made for a new data object. If the beforeload handler returns false
13260 * the load action will be canceled.
13261 * @param {Store} this
13262 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266 * @event beforeloadadd
13267 * Fires after a new set of Records has been loaded.
13268 * @param {Store} this
13269 * @param {Roo.data.Record[]} records The Records that were loaded
13270 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13272 beforeloadadd : true,
13275 * Fires after a new set of Records has been loaded, before they are added to the store.
13276 * @param {Store} this
13277 * @param {Roo.data.Record[]} records The Records that were loaded
13278 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13279 * @params {Object} return from reader
13283 * @event loadexception
13284 * Fires if an exception occurs in the Proxy during loading.
13285 * Called with the signature of the Proxy's "loadexception" event.
13286 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13289 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13290 * @param {Object} load options
13291 * @param {Object} jsonData from your request (normally this contains the Exception)
13293 loadexception : true
13297 this.proxy = Roo.factory(this.proxy, Roo.data);
13298 this.proxy.xmodule = this.xmodule || false;
13299 this.relayEvents(this.proxy, ["loadexception"]);
13301 this.sortToggle = {};
13302 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13304 Roo.data.Store.superclass.constructor.call(this);
13306 if(this.inlineData){
13307 this.loadData(this.inlineData);
13308 delete this.inlineData;
13312 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13314 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13315 * without a remote query - used by combo/forms at present.
13319 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13322 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13325 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13326 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13329 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13330 * on any HTTP request
13333 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13336 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13340 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13341 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13343 remoteSort : false,
13346 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13347 * loaded or when a record is removed. (defaults to false).
13349 pruneModifiedRecords : false,
13352 lastOptions : null,
13355 * Add Records to the Store and fires the add event.
13356 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13358 add : function(records){
13359 records = [].concat(records);
13360 for(var i = 0, len = records.length; i < len; i++){
13361 records[i].join(this);
13363 var index = this.data.length;
13364 this.data.addAll(records);
13365 this.fireEvent("add", this, records, index);
13369 * Remove a Record from the Store and fires the remove event.
13370 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13372 remove : function(record){
13373 var index = this.data.indexOf(record);
13374 this.data.removeAt(index);
13376 if(this.pruneModifiedRecords){
13377 this.modified.remove(record);
13379 this.fireEvent("remove", this, record, index);
13383 * Remove all Records from the Store and fires the clear event.
13385 removeAll : function(){
13387 if(this.pruneModifiedRecords){
13388 this.modified = [];
13390 this.fireEvent("clear", this);
13394 * Inserts Records to the Store at the given index and fires the add event.
13395 * @param {Number} index The start index at which to insert the passed Records.
13396 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13398 insert : function(index, records){
13399 records = [].concat(records);
13400 for(var i = 0, len = records.length; i < len; i++){
13401 this.data.insert(index, records[i]);
13402 records[i].join(this);
13404 this.fireEvent("add", this, records, index);
13408 * Get the index within the cache of the passed Record.
13409 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13410 * @return {Number} The index of the passed Record. Returns -1 if not found.
13412 indexOf : function(record){
13413 return this.data.indexOf(record);
13417 * Get the index within the cache of the Record with the passed id.
13418 * @param {String} id The id of the Record to find.
13419 * @return {Number} The index of the Record. Returns -1 if not found.
13421 indexOfId : function(id){
13422 return this.data.indexOfKey(id);
13426 * Get the Record with the specified id.
13427 * @param {String} id The id of the Record to find.
13428 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13430 getById : function(id){
13431 return this.data.key(id);
13435 * Get the Record at the specified index.
13436 * @param {Number} index The index of the Record to find.
13437 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13439 getAt : function(index){
13440 return this.data.itemAt(index);
13444 * Returns a range of Records between specified indices.
13445 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13446 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13447 * @return {Roo.data.Record[]} An array of Records
13449 getRange : function(start, end){
13450 return this.data.getRange(start, end);
13454 storeOptions : function(o){
13455 o = Roo.apply({}, o);
13458 this.lastOptions = o;
13462 * Loads the Record cache from the configured Proxy using the configured Reader.
13464 * If using remote paging, then the first load call must specify the <em>start</em>
13465 * and <em>limit</em> properties in the options.params property to establish the initial
13466 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13468 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13469 * and this call will return before the new data has been loaded. Perform any post-processing
13470 * in a callback function, or in a "load" event handler.</strong>
13472 * @param {Object} options An object containing properties which control loading options:<ul>
13473 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13474 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13475 * passed the following arguments:<ul>
13476 * <li>r : Roo.data.Record[]</li>
13477 * <li>options: Options object from the load call</li>
13478 * <li>success: Boolean success indicator</li></ul></li>
13479 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13480 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13483 load : function(options){
13484 options = options || {};
13485 if(this.fireEvent("beforeload", this, options) !== false){
13486 this.storeOptions(options);
13487 var p = Roo.apply(options.params || {}, this.baseParams);
13488 // if meta was not loaded from remote source.. try requesting it.
13489 if (!this.reader.metaFromRemote) {
13490 p._requestMeta = 1;
13492 if(this.sortInfo && this.remoteSort){
13493 var pn = this.paramNames;
13494 p[pn["sort"]] = this.sortInfo.field;
13495 p[pn["dir"]] = this.sortInfo.direction;
13497 if (this.multiSort) {
13498 var pn = this.paramNames;
13499 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13502 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13507 * Reloads the Record cache from the configured Proxy using the configured Reader and
13508 * the options from the last load operation performed.
13509 * @param {Object} options (optional) An object containing properties which may override the options
13510 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13511 * the most recently used options are reused).
13513 reload : function(options){
13514 this.load(Roo.applyIf(options||{}, this.lastOptions));
13518 // Called as a callback by the Reader during a load operation.
13519 loadRecords : function(o, options, success){
13520 if(!o || success === false){
13521 if(success !== false){
13522 this.fireEvent("load", this, [], options, o);
13524 if(options.callback){
13525 options.callback.call(options.scope || this, [], options, false);
13529 // if data returned failure - throw an exception.
13530 if (o.success === false) {
13531 // show a message if no listener is registered.
13532 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13533 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13535 // loadmask wil be hooked into this..
13536 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13539 var r = o.records, t = o.totalRecords || r.length;
13541 this.fireEvent("beforeloadadd", this, r, options, o);
13543 if(!options || options.add !== true){
13544 if(this.pruneModifiedRecords){
13545 this.modified = [];
13547 for(var i = 0, len = r.length; i < len; i++){
13551 this.data = this.snapshot;
13552 delete this.snapshot;
13555 this.data.addAll(r);
13556 this.totalLength = t;
13558 this.fireEvent("datachanged", this);
13560 this.totalLength = Math.max(t, this.data.length+r.length);
13564 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13566 var e = new Roo.data.Record({});
13568 e.set(this.parent.displayField, this.parent.emptyTitle);
13569 e.set(this.parent.valueField, '');
13574 this.fireEvent("load", this, r, options, o);
13575 if(options.callback){
13576 options.callback.call(options.scope || this, r, options, true);
13582 * Loads data from a passed data block. A Reader which understands the format of the data
13583 * must have been configured in the constructor.
13584 * @param {Object} data The data block from which to read the Records. The format of the data expected
13585 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13586 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13588 loadData : function(o, append){
13589 var r = this.reader.readRecords(o);
13590 this.loadRecords(r, {add: append}, true);
13594 * using 'cn' the nested child reader read the child array into it's child stores.
13595 * @param {Object} rec The record with a 'children array
13597 loadDataFromChildren : function(rec)
13599 this.loadData(this.reader.toLoadData(rec));
13604 * Gets the number of cached records.
13606 * <em>If using paging, this may not be the total size of the dataset. If the data object
13607 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13608 * the data set size</em>
13610 getCount : function(){
13611 return this.data.length || 0;
13615 * Gets the total number of records in the dataset as returned by the server.
13617 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13618 * the dataset size</em>
13620 getTotalCount : function(){
13621 return this.totalLength || 0;
13625 * Returns the sort state of the Store as an object with two properties:
13627 field {String} The name of the field by which the Records are sorted
13628 direction {String} The sort order, "ASC" or "DESC"
13631 getSortState : function(){
13632 return this.sortInfo;
13636 applySort : function(){
13637 if(this.sortInfo && !this.remoteSort){
13638 var s = this.sortInfo, f = s.field;
13639 var st = this.fields.get(f).sortType;
13640 var fn = function(r1, r2){
13641 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13642 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13644 this.data.sort(s.direction, fn);
13645 if(this.snapshot && this.snapshot != this.data){
13646 this.snapshot.sort(s.direction, fn);
13652 * Sets the default sort column and order to be used by the next load operation.
13653 * @param {String} fieldName The name of the field to sort by.
13654 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13656 setDefaultSort : function(field, dir){
13657 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13661 * Sort the Records.
13662 * If remote sorting is used, the sort is performed on the server, and the cache is
13663 * reloaded. If local sorting is used, the cache is sorted internally.
13664 * @param {String} fieldName The name of the field to sort by.
13665 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13667 sort : function(fieldName, dir){
13668 var f = this.fields.get(fieldName);
13670 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13672 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13673 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13678 this.sortToggle[f.name] = dir;
13679 this.sortInfo = {field: f.name, direction: dir};
13680 if(!this.remoteSort){
13682 this.fireEvent("datachanged", this);
13684 this.load(this.lastOptions);
13689 * Calls the specified function for each of the Records in the cache.
13690 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13691 * Returning <em>false</em> aborts and exits the iteration.
13692 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13694 each : function(fn, scope){
13695 this.data.each(fn, scope);
13699 * Gets all records modified since the last commit. Modified records are persisted across load operations
13700 * (e.g., during paging).
13701 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13703 getModifiedRecords : function(){
13704 return this.modified;
13708 createFilterFn : function(property, value, anyMatch){
13709 if(!value.exec){ // not a regex
13710 value = String(value);
13711 if(value.length == 0){
13714 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13716 return function(r){
13717 return value.test(r.data[property]);
13722 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13723 * @param {String} property A field on your records
13724 * @param {Number} start The record index to start at (defaults to 0)
13725 * @param {Number} end The last record index to include (defaults to length - 1)
13726 * @return {Number} The sum
13728 sum : function(property, start, end){
13729 var rs = this.data.items, v = 0;
13730 start = start || 0;
13731 end = (end || end === 0) ? end : rs.length-1;
13733 for(var i = start; i <= end; i++){
13734 v += (rs[i].data[property] || 0);
13740 * Filter the records by a specified property.
13741 * @param {String} field A field on your records
13742 * @param {String/RegExp} value Either a string that the field
13743 * should start with or a RegExp to test against the field
13744 * @param {Boolean} anyMatch True to match any part not just the beginning
13746 filter : function(property, value, anyMatch){
13747 var fn = this.createFilterFn(property, value, anyMatch);
13748 return fn ? this.filterBy(fn) : this.clearFilter();
13752 * Filter by a function. The specified function will be called with each
13753 * record in this data source. If the function returns true the record is included,
13754 * otherwise it is filtered.
13755 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13756 * @param {Object} scope (optional) The scope of the function (defaults to this)
13758 filterBy : function(fn, scope){
13759 this.snapshot = this.snapshot || this.data;
13760 this.data = this.queryBy(fn, scope||this);
13761 this.fireEvent("datachanged", this);
13765 * Query the records by a specified property.
13766 * @param {String} field A field on your records
13767 * @param {String/RegExp} value Either a string that the field
13768 * should start with or a RegExp to test against the field
13769 * @param {Boolean} anyMatch True to match any part not just the beginning
13770 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13772 query : function(property, value, anyMatch){
13773 var fn = this.createFilterFn(property, value, anyMatch);
13774 return fn ? this.queryBy(fn) : this.data.clone();
13778 * Query by a function. The specified function will be called with each
13779 * record in this data source. If the function returns true the record is included
13781 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13782 * @param {Object} scope (optional) The scope of the function (defaults to this)
13783 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13785 queryBy : function(fn, scope){
13786 var data = this.snapshot || this.data;
13787 return data.filterBy(fn, scope||this);
13791 * Collects unique values for a particular dataIndex from this store.
13792 * @param {String} dataIndex The property to collect
13793 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13794 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13795 * @return {Array} An array of the unique values
13797 collect : function(dataIndex, allowNull, bypassFilter){
13798 var d = (bypassFilter === true && this.snapshot) ?
13799 this.snapshot.items : this.data.items;
13800 var v, sv, r = [], l = {};
13801 for(var i = 0, len = d.length; i < len; i++){
13802 v = d[i].data[dataIndex];
13804 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13813 * Revert to a view of the Record cache with no filtering applied.
13814 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13816 clearFilter : function(suppressEvent){
13817 if(this.snapshot && this.snapshot != this.data){
13818 this.data = this.snapshot;
13819 delete this.snapshot;
13820 if(suppressEvent !== true){
13821 this.fireEvent("datachanged", this);
13827 afterEdit : function(record){
13828 if(this.modified.indexOf(record) == -1){
13829 this.modified.push(record);
13831 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13835 afterReject : function(record){
13836 this.modified.remove(record);
13837 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13841 afterCommit : function(record){
13842 this.modified.remove(record);
13843 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13847 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13848 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13850 commitChanges : function(){
13851 var m = this.modified.slice(0);
13852 this.modified = [];
13853 for(var i = 0, len = m.length; i < len; i++){
13859 * Cancel outstanding changes on all changed records.
13861 rejectChanges : function(){
13862 var m = this.modified.slice(0);
13863 this.modified = [];
13864 for(var i = 0, len = m.length; i < len; i++){
13869 onMetaChange : function(meta, rtype, o){
13870 this.recordType = rtype;
13871 this.fields = rtype.prototype.fields;
13872 delete this.snapshot;
13873 this.sortInfo = meta.sortInfo || this.sortInfo;
13874 this.modified = [];
13875 this.fireEvent('metachange', this, this.reader.meta);
13878 moveIndex : function(data, type)
13880 var index = this.indexOf(data);
13882 var newIndex = index + type;
13886 this.insert(newIndex, data);
13891 * Ext JS Library 1.1.1
13892 * Copyright(c) 2006-2007, Ext JS, LLC.
13894 * Originally Released Under LGPL - original licence link has changed is not relivant.
13897 * <script type="text/javascript">
13901 * @class Roo.data.SimpleStore
13902 * @extends Roo.data.Store
13903 * Small helper class to make creating Stores from Array data easier.
13904 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13905 * @cfg {Array} fields An array of field definition objects, or field name strings.
13906 * @cfg {Object} an existing reader (eg. copied from another store)
13907 * @cfg {Array} data The multi-dimensional array of data
13909 * @param {Object} config
13911 Roo.data.SimpleStore = function(config)
13913 Roo.data.SimpleStore.superclass.constructor.call(this, {
13915 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13918 Roo.data.Record.create(config.fields)
13920 proxy : new Roo.data.MemoryProxy(config.data)
13924 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13926 * Ext JS Library 1.1.1
13927 * Copyright(c) 2006-2007, Ext JS, LLC.
13929 * Originally Released Under LGPL - original licence link has changed is not relivant.
13932 * <script type="text/javascript">
13937 * @extends Roo.data.Store
13938 * @class Roo.data.JsonStore
13939 * Small helper class to make creating Stores for JSON data easier. <br/>
13941 var store = new Roo.data.JsonStore({
13942 url: 'get-images.php',
13944 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13947 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13948 * JsonReader and HttpProxy (unless inline data is provided).</b>
13949 * @cfg {Array} fields An array of field definition objects, or field name strings.
13951 * @param {Object} config
13953 Roo.data.JsonStore = function(c){
13954 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13955 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13956 reader: new Roo.data.JsonReader(c, c.fields)
13959 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13961 * Ext JS Library 1.1.1
13962 * Copyright(c) 2006-2007, Ext JS, LLC.
13964 * Originally Released Under LGPL - original licence link has changed is not relivant.
13967 * <script type="text/javascript">
13971 Roo.data.Field = function(config){
13972 if(typeof config == "string"){
13973 config = {name: config};
13975 Roo.apply(this, config);
13978 this.type = "auto";
13981 var st = Roo.data.SortTypes;
13982 // named sortTypes are supported, here we look them up
13983 if(typeof this.sortType == "string"){
13984 this.sortType = st[this.sortType];
13987 // set default sortType for strings and dates
13988 if(!this.sortType){
13991 this.sortType = st.asUCString;
13994 this.sortType = st.asDate;
13997 this.sortType = st.none;
14002 var stripRe = /[\$,%]/g;
14004 // prebuilt conversion function for this field, instead of
14005 // switching every time we're reading a value
14007 var cv, dateFormat = this.dateFormat;
14012 cv = function(v){ return v; };
14015 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14019 return v !== undefined && v !== null && v !== '' ?
14020 parseInt(String(v).replace(stripRe, ""), 10) : '';
14025 return v !== undefined && v !== null && v !== '' ?
14026 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14031 cv = function(v){ return v === true || v === "true" || v == 1; };
14038 if(v instanceof Date){
14042 if(dateFormat == "timestamp"){
14043 return new Date(v*1000);
14045 return Date.parseDate(v, dateFormat);
14047 var parsed = Date.parse(v);
14048 return parsed ? new Date(parsed) : null;
14057 Roo.data.Field.prototype = {
14065 * Ext JS Library 1.1.1
14066 * Copyright(c) 2006-2007, Ext JS, LLC.
14068 * Originally Released Under LGPL - original licence link has changed is not relivant.
14071 * <script type="text/javascript">
14074 // Base class for reading structured data from a data source. This class is intended to be
14075 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14078 * @class Roo.data.DataReader
14079 * Base class for reading structured data from a data source. This class is intended to be
14080 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14083 Roo.data.DataReader = function(meta, recordType){
14087 this.recordType = recordType instanceof Array ?
14088 Roo.data.Record.create(recordType) : recordType;
14091 Roo.data.DataReader.prototype = {
14094 readerType : 'Data',
14096 * Create an empty record
14097 * @param {Object} data (optional) - overlay some values
14098 * @return {Roo.data.Record} record created.
14100 newRow : function(d) {
14102 this.recordType.prototype.fields.each(function(c) {
14104 case 'int' : da[c.name] = 0; break;
14105 case 'date' : da[c.name] = new Date(); break;
14106 case 'float' : da[c.name] = 0.0; break;
14107 case 'boolean' : da[c.name] = false; break;
14108 default : da[c.name] = ""; break;
14112 return new this.recordType(Roo.apply(da, d));
14118 * Ext JS Library 1.1.1
14119 * Copyright(c) 2006-2007, Ext JS, LLC.
14121 * Originally Released Under LGPL - original licence link has changed is not relivant.
14124 * <script type="text/javascript">
14128 * @class Roo.data.DataProxy
14129 * @extends Roo.data.Observable
14130 * This class is an abstract base class for implementations which provide retrieval of
14131 * unformatted data objects.<br>
14133 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14134 * (of the appropriate type which knows how to parse the data object) to provide a block of
14135 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14137 * Custom implementations must implement the load method as described in
14138 * {@link Roo.data.HttpProxy#load}.
14140 Roo.data.DataProxy = function(){
14143 * @event beforeload
14144 * Fires before a network request is made to retrieve a data object.
14145 * @param {Object} This DataProxy object.
14146 * @param {Object} params The params parameter to the load function.
14151 * Fires before the load method's callback is called.
14152 * @param {Object} This DataProxy object.
14153 * @param {Object} o The data object.
14154 * @param {Object} arg The callback argument object passed to the load function.
14158 * @event loadexception
14159 * Fires if an Exception occurs during data retrieval.
14160 * @param {Object} This DataProxy object.
14161 * @param {Object} o The data object.
14162 * @param {Object} arg The callback argument object passed to the load function.
14163 * @param {Object} e The Exception.
14165 loadexception : true
14167 Roo.data.DataProxy.superclass.constructor.call(this);
14170 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14173 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14177 * Ext JS Library 1.1.1
14178 * Copyright(c) 2006-2007, Ext JS, LLC.
14180 * Originally Released Under LGPL - original licence link has changed is not relivant.
14183 * <script type="text/javascript">
14186 * @class Roo.data.MemoryProxy
14187 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14188 * to the Reader when its load method is called.
14190 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14192 Roo.data.MemoryProxy = function(data){
14196 Roo.data.MemoryProxy.superclass.constructor.call(this);
14200 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14203 * Load data from the requested source (in this case an in-memory
14204 * data object passed to the constructor), read the data object into
14205 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14206 * process that block using the passed callback.
14207 * @param {Object} params This parameter is not used by the MemoryProxy class.
14208 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14209 * object into a block of Roo.data.Records.
14210 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14211 * The function must be passed <ul>
14212 * <li>The Record block object</li>
14213 * <li>The "arg" argument from the load function</li>
14214 * <li>A boolean success indicator</li>
14216 * @param {Object} scope The scope in which to call the callback
14217 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14219 load : function(params, reader, callback, scope, arg){
14220 params = params || {};
14223 result = reader.readRecords(params.data ? params.data :this.data);
14225 this.fireEvent("loadexception", this, arg, null, e);
14226 callback.call(scope, null, arg, false);
14229 callback.call(scope, result, arg, true);
14233 update : function(params, records){
14238 * Ext JS Library 1.1.1
14239 * Copyright(c) 2006-2007, Ext JS, LLC.
14241 * Originally Released Under LGPL - original licence link has changed is not relivant.
14244 * <script type="text/javascript">
14247 * @class Roo.data.HttpProxy
14248 * @extends Roo.data.DataProxy
14249 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14250 * configured to reference a certain URL.<br><br>
14252 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14253 * from which the running page was served.<br><br>
14255 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14257 * Be aware that to enable the browser to parse an XML document, the server must set
14258 * the Content-Type header in the HTTP response to "text/xml".
14260 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14261 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14262 * will be used to make the request.
14264 Roo.data.HttpProxy = function(conn){
14265 Roo.data.HttpProxy.superclass.constructor.call(this);
14266 // is conn a conn config or a real conn?
14268 this.useAjax = !conn || !conn.events;
14272 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14273 // thse are take from connection...
14276 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14279 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14280 * extra parameters to each request made by this object. (defaults to undefined)
14283 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14284 * to each request made by this object. (defaults to undefined)
14287 * @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)
14290 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14293 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14299 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14303 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14304 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14305 * a finer-grained basis than the DataProxy events.
14307 getConnection : function(){
14308 return this.useAjax ? Roo.Ajax : this.conn;
14312 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14313 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14314 * process that block using the passed callback.
14315 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14316 * for the request to the remote server.
14317 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14318 * object into a block of Roo.data.Records.
14319 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14320 * The function must be passed <ul>
14321 * <li>The Record block object</li>
14322 * <li>The "arg" argument from the load function</li>
14323 * <li>A boolean success indicator</li>
14325 * @param {Object} scope The scope in which to call the callback
14326 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14328 load : function(params, reader, callback, scope, arg){
14329 if(this.fireEvent("beforeload", this, params) !== false){
14331 params : params || {},
14333 callback : callback,
14338 callback : this.loadResponse,
14342 Roo.applyIf(o, this.conn);
14343 if(this.activeRequest){
14344 Roo.Ajax.abort(this.activeRequest);
14346 this.activeRequest = Roo.Ajax.request(o);
14348 this.conn.request(o);
14351 callback.call(scope||this, null, arg, false);
14356 loadResponse : function(o, success, response){
14357 delete this.activeRequest;
14359 this.fireEvent("loadexception", this, o, response);
14360 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14365 result = o.reader.read(response);
14367 this.fireEvent("loadexception", this, o, response, e);
14368 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14372 this.fireEvent("load", this, o, o.request.arg);
14373 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14377 update : function(dataSet){
14382 updateResponse : function(dataSet){
14387 * Ext JS Library 1.1.1
14388 * Copyright(c) 2006-2007, Ext JS, LLC.
14390 * Originally Released Under LGPL - original licence link has changed is not relivant.
14393 * <script type="text/javascript">
14397 * @class Roo.data.ScriptTagProxy
14398 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14399 * other than the originating domain of the running page.<br><br>
14401 * <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
14402 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14404 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14405 * source code that is used as the source inside a <script> tag.<br><br>
14407 * In order for the browser to process the returned data, the server must wrap the data object
14408 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14409 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14410 * depending on whether the callback name was passed:
14413 boolean scriptTag = false;
14414 String cb = request.getParameter("callback");
14417 response.setContentType("text/javascript");
14419 response.setContentType("application/x-json");
14421 Writer out = response.getWriter();
14423 out.write(cb + "(");
14425 out.print(dataBlock.toJsonString());
14432 * @param {Object} config A configuration object.
14434 Roo.data.ScriptTagProxy = function(config){
14435 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14436 Roo.apply(this, config);
14437 this.head = document.getElementsByTagName("head")[0];
14440 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14442 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14444 * @cfg {String} url The URL from which to request the data object.
14447 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14451 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14452 * the server the name of the callback function set up by the load call to process the returned data object.
14453 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14454 * javascript output which calls this named function passing the data object as its only parameter.
14456 callbackParam : "callback",
14458 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14459 * name to the request.
14464 * Load data from the configured URL, read the data object into
14465 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14466 * process that block using the passed callback.
14467 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14468 * for the request to the remote server.
14469 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14470 * object into a block of Roo.data.Records.
14471 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14472 * The function must be passed <ul>
14473 * <li>The Record block object</li>
14474 * <li>The "arg" argument from the load function</li>
14475 * <li>A boolean success indicator</li>
14477 * @param {Object} scope The scope in which to call the callback
14478 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14480 load : function(params, reader, callback, scope, arg){
14481 if(this.fireEvent("beforeload", this, params) !== false){
14483 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14485 var url = this.url;
14486 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14488 url += "&_dc=" + (new Date().getTime());
14490 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14493 cb : "stcCallback"+transId,
14494 scriptId : "stcScript"+transId,
14498 callback : callback,
14504 window[trans.cb] = function(o){
14505 conn.handleResponse(o, trans);
14508 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14510 if(this.autoAbort !== false){
14514 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14516 var script = document.createElement("script");
14517 script.setAttribute("src", url);
14518 script.setAttribute("type", "text/javascript");
14519 script.setAttribute("id", trans.scriptId);
14520 this.head.appendChild(script);
14522 this.trans = trans;
14524 callback.call(scope||this, null, arg, false);
14529 isLoading : function(){
14530 return this.trans ? true : false;
14534 * Abort the current server request.
14536 abort : function(){
14537 if(this.isLoading()){
14538 this.destroyTrans(this.trans);
14543 destroyTrans : function(trans, isLoaded){
14544 this.head.removeChild(document.getElementById(trans.scriptId));
14545 clearTimeout(trans.timeoutId);
14547 window[trans.cb] = undefined;
14549 delete window[trans.cb];
14552 // if hasn't been loaded, wait for load to remove it to prevent script error
14553 window[trans.cb] = function(){
14554 window[trans.cb] = undefined;
14556 delete window[trans.cb];
14563 handleResponse : function(o, trans){
14564 this.trans = false;
14565 this.destroyTrans(trans, true);
14568 result = trans.reader.readRecords(o);
14570 this.fireEvent("loadexception", this, o, trans.arg, e);
14571 trans.callback.call(trans.scope||window, null, trans.arg, false);
14574 this.fireEvent("load", this, o, trans.arg);
14575 trans.callback.call(trans.scope||window, result, trans.arg, true);
14579 handleFailure : function(trans){
14580 this.trans = false;
14581 this.destroyTrans(trans, false);
14582 this.fireEvent("loadexception", this, null, trans.arg);
14583 trans.callback.call(trans.scope||window, null, trans.arg, false);
14587 * Ext JS Library 1.1.1
14588 * Copyright(c) 2006-2007, Ext JS, LLC.
14590 * Originally Released Under LGPL - original licence link has changed is not relivant.
14593 * <script type="text/javascript">
14597 * @class Roo.data.JsonReader
14598 * @extends Roo.data.DataReader
14599 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14600 * based on mappings in a provided Roo.data.Record constructor.
14602 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14603 * in the reply previously.
14608 var RecordDef = Roo.data.Record.create([
14609 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14610 {name: 'occupation'} // This field will use "occupation" as the mapping.
14612 var myReader = new Roo.data.JsonReader({
14613 totalProperty: "results", // The property which contains the total dataset size (optional)
14614 root: "rows", // The property which contains an Array of row objects
14615 id: "id" // The property within each row object that provides an ID for the record (optional)
14619 * This would consume a JSON file like this:
14621 { 'results': 2, 'rows': [
14622 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14623 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14626 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14627 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14628 * paged from the remote server.
14629 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14630 * @cfg {String} root name of the property which contains the Array of row objects.
14631 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14632 * @cfg {Array} fields Array of field definition objects
14634 * Create a new JsonReader
14635 * @param {Object} meta Metadata configuration options
14636 * @param {Object} recordType Either an Array of field definition objects,
14637 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14639 Roo.data.JsonReader = function(meta, recordType){
14642 // set some defaults:
14643 Roo.applyIf(meta, {
14644 totalProperty: 'total',
14645 successProperty : 'success',
14650 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14652 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14654 readerType : 'Json',
14657 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14658 * Used by Store query builder to append _requestMeta to params.
14661 metaFromRemote : false,
14663 * This method is only used by a DataProxy which has retrieved data from a remote server.
14664 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14665 * @return {Object} data A data block which is used by an Roo.data.Store object as
14666 * a cache of Roo.data.Records.
14668 read : function(response){
14669 var json = response.responseText;
14671 var o = /* eval:var:o */ eval("("+json+")");
14673 throw {message: "JsonReader.read: Json object not found"};
14679 this.metaFromRemote = true;
14680 this.meta = o.metaData;
14681 this.recordType = Roo.data.Record.create(o.metaData.fields);
14682 this.onMetaChange(this.meta, this.recordType, o);
14684 return this.readRecords(o);
14687 // private function a store will implement
14688 onMetaChange : function(meta, recordType, o){
14695 simpleAccess: function(obj, subsc) {
14702 getJsonAccessor: function(){
14704 return function(expr) {
14706 return(re.test(expr))
14707 ? new Function("obj", "return obj." + expr)
14712 return Roo.emptyFn;
14717 * Create a data block containing Roo.data.Records from an XML document.
14718 * @param {Object} o An object which contains an Array of row objects in the property specified
14719 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14720 * which contains the total size of the dataset.
14721 * @return {Object} data A data block which is used by an Roo.data.Store object as
14722 * a cache of Roo.data.Records.
14724 readRecords : function(o){
14726 * After any data loads, the raw JSON data is available for further custom processing.
14730 var s = this.meta, Record = this.recordType,
14731 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14733 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14735 if(s.totalProperty) {
14736 this.getTotal = this.getJsonAccessor(s.totalProperty);
14738 if(s.successProperty) {
14739 this.getSuccess = this.getJsonAccessor(s.successProperty);
14741 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14743 var g = this.getJsonAccessor(s.id);
14744 this.getId = function(rec) {
14746 return (r === undefined || r === "") ? null : r;
14749 this.getId = function(){return null;};
14752 for(var jj = 0; jj < fl; jj++){
14754 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14755 this.ef[jj] = this.getJsonAccessor(map);
14759 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14760 if(s.totalProperty){
14761 var vt = parseInt(this.getTotal(o), 10);
14766 if(s.successProperty){
14767 var vs = this.getSuccess(o);
14768 if(vs === false || vs === 'false'){
14773 for(var i = 0; i < c; i++){
14776 var id = this.getId(n);
14777 for(var j = 0; j < fl; j++){
14779 var v = this.ef[j](n);
14781 Roo.log('missing convert for ' + f.name);
14785 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14787 var record = new Record(values, id);
14789 records[i] = record;
14795 totalRecords : totalRecords
14798 // used when loading children.. @see loadDataFromChildren
14799 toLoadData: function(rec)
14801 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14802 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14803 return { data : data, total : data.length };
14808 * Ext JS Library 1.1.1
14809 * Copyright(c) 2006-2007, Ext JS, LLC.
14811 * Originally Released Under LGPL - original licence link has changed is not relivant.
14814 * <script type="text/javascript">
14818 * @class Roo.data.ArrayReader
14819 * @extends Roo.data.DataReader
14820 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14821 * Each element of that Array represents a row of data fields. The
14822 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14823 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14827 var RecordDef = Roo.data.Record.create([
14828 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14829 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14831 var myReader = new Roo.data.ArrayReader({
14832 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14836 * This would consume an Array like this:
14838 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14842 * Create a new JsonReader
14843 * @param {Object} meta Metadata configuration options.
14844 * @param {Object|Array} recordType Either an Array of field definition objects
14846 * @cfg {Array} fields Array of field definition objects
14847 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14848 * as specified to {@link Roo.data.Record#create},
14849 * or an {@link Roo.data.Record} object
14852 * created using {@link Roo.data.Record#create}.
14854 Roo.data.ArrayReader = function(meta, recordType)
14856 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14859 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14862 * Create a data block containing Roo.data.Records from an XML document.
14863 * @param {Object} o An Array of row objects which represents the dataset.
14864 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14865 * a cache of Roo.data.Records.
14867 readRecords : function(o)
14869 var sid = this.meta ? this.meta.id : null;
14870 var recordType = this.recordType, fields = recordType.prototype.fields;
14873 for(var i = 0; i < root.length; i++){
14876 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14877 for(var j = 0, jlen = fields.length; j < jlen; j++){
14878 var f = fields.items[j];
14879 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14880 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14882 values[f.name] = v;
14884 var record = new recordType(values, id);
14886 records[records.length] = record;
14890 totalRecords : records.length
14893 // used when loading children.. @see loadDataFromChildren
14894 toLoadData: function(rec)
14896 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14897 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14908 * @class Roo.bootstrap.ComboBox
14909 * @extends Roo.bootstrap.TriggerField
14910 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14911 * @cfg {Boolean} append (true|false) default false
14912 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14913 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14914 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14915 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14916 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14917 * @cfg {Boolean} animate default true
14918 * @cfg {Boolean} emptyResultText only for touch device
14919 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14920 * @cfg {String} emptyTitle default ''
14921 * @cfg {Number} width fixed with? experimental
14923 * Create a new ComboBox.
14924 * @param {Object} config Configuration options
14926 Roo.bootstrap.ComboBox = function(config){
14927 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14931 * Fires when the dropdown list is expanded
14932 * @param {Roo.bootstrap.ComboBox} combo This combo box
14937 * Fires when the dropdown list is collapsed
14938 * @param {Roo.bootstrap.ComboBox} combo This combo box
14942 * @event beforeselect
14943 * Fires before a list item is selected. Return false to cancel the selection.
14944 * @param {Roo.bootstrap.ComboBox} combo This combo box
14945 * @param {Roo.data.Record} record The data record returned from the underlying store
14946 * @param {Number} index The index of the selected item in the dropdown list
14948 'beforeselect' : true,
14951 * Fires when a list item is selected
14952 * @param {Roo.bootstrap.ComboBox} combo This combo box
14953 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14954 * @param {Number} index The index of the selected item in the dropdown list
14958 * @event beforequery
14959 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14960 * The event object passed has these properties:
14961 * @param {Roo.bootstrap.ComboBox} combo This combo box
14962 * @param {String} query The query
14963 * @param {Boolean} forceAll true to force "all" query
14964 * @param {Boolean} cancel true to cancel the query
14965 * @param {Object} e The query event object
14967 'beforequery': true,
14970 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14971 * @param {Roo.bootstrap.ComboBox} combo This combo box
14976 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14977 * @param {Roo.bootstrap.ComboBox} combo This combo box
14978 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14983 * Fires when the remove value from the combobox array
14984 * @param {Roo.bootstrap.ComboBox} combo This combo box
14988 * @event afterremove
14989 * Fires when the remove value from the combobox array
14990 * @param {Roo.bootstrap.ComboBox} combo This combo box
14992 'afterremove' : true,
14994 * @event specialfilter
14995 * Fires when specialfilter
14996 * @param {Roo.bootstrap.ComboBox} combo This combo box
14998 'specialfilter' : true,
15001 * Fires when tick the element
15002 * @param {Roo.bootstrap.ComboBox} combo This combo box
15006 * @event touchviewdisplay
15007 * Fires when touch view require special display (default is using displayField)
15008 * @param {Roo.bootstrap.ComboBox} combo This combo box
15009 * @param {Object} cfg set html .
15011 'touchviewdisplay' : true
15016 this.tickItems = [];
15018 this.selectedIndex = -1;
15019 if(this.mode == 'local'){
15020 if(config.queryDelay === undefined){
15021 this.queryDelay = 10;
15023 if(config.minChars === undefined){
15029 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15032 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15033 * rendering into an Roo.Editor, defaults to false)
15036 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15037 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15040 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15043 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15044 * the dropdown list (defaults to undefined, with no header element)
15048 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15052 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15054 listWidth: undefined,
15056 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15057 * mode = 'remote' or 'text' if mode = 'local')
15059 displayField: undefined,
15062 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15063 * mode = 'remote' or 'value' if mode = 'local').
15064 * Note: use of a valueField requires the user make a selection
15065 * in order for a value to be mapped.
15067 valueField: undefined,
15069 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15074 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15075 * field's data value (defaults to the underlying DOM element's name)
15077 hiddenName: undefined,
15079 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15083 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15085 selectedClass: 'active',
15088 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15092 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15093 * anchor positions (defaults to 'tl-bl')
15095 listAlign: 'tl-bl?',
15097 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15101 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15102 * query specified by the allQuery config option (defaults to 'query')
15104 triggerAction: 'query',
15106 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15107 * (defaults to 4, does not apply if editable = false)
15111 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15112 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15116 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15117 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15121 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15122 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15126 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15127 * when editable = true (defaults to false)
15129 selectOnFocus:false,
15131 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15133 queryParam: 'query',
15135 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15136 * when mode = 'remote' (defaults to 'Loading...')
15138 loadingText: 'Loading...',
15140 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15144 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15148 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15149 * traditional select (defaults to true)
15153 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15157 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15161 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15162 * listWidth has a higher value)
15166 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15167 * allow the user to set arbitrary text into the field (defaults to false)
15169 forceSelection:false,
15171 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15172 * if typeAhead = true (defaults to 250)
15174 typeAheadDelay : 250,
15176 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15177 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15179 valueNotFoundText : undefined,
15181 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15183 blockFocus : false,
15186 * @cfg {Boolean} disableClear Disable showing of clear button.
15188 disableClear : false,
15190 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15192 alwaysQuery : false,
15195 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15200 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15202 invalidClass : "has-warning",
15205 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15207 validClass : "has-success",
15210 * @cfg {Boolean} specialFilter (true|false) special filter default false
15212 specialFilter : false,
15215 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15217 mobileTouchView : true,
15220 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15222 useNativeIOS : false,
15225 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15227 mobile_restrict_height : false,
15229 ios_options : false,
15241 btnPosition : 'right',
15242 triggerList : true,
15243 showToggleBtn : true,
15245 emptyResultText: 'Empty',
15246 triggerText : 'Select',
15250 // element that contains real text value.. (when hidden is used..)
15252 getAutoCreate : function()
15257 * Render classic select for iso
15260 if(Roo.isIOS && this.useNativeIOS){
15261 cfg = this.getAutoCreateNativeIOS();
15269 if(Roo.isTouch && this.mobileTouchView){
15270 cfg = this.getAutoCreateTouchView();
15277 if(!this.tickable){
15278 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15283 * ComboBox with tickable selections
15286 var align = this.labelAlign || this.parentLabelAlign();
15289 cls : 'form-group roo-combobox-tickable' //input-group
15292 var btn_text_select = '';
15293 var btn_text_done = '';
15294 var btn_text_cancel = '';
15296 if (this.btn_text_show) {
15297 btn_text_select = 'Select';
15298 btn_text_done = 'Done';
15299 btn_text_cancel = 'Cancel';
15304 cls : 'tickable-buttons',
15309 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15310 //html : this.triggerText
15311 html: btn_text_select
15317 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15319 html: btn_text_done
15325 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15327 html: btn_text_cancel
15333 buttons.cn.unshift({
15335 cls: 'roo-select2-search-field-input'
15341 Roo.each(buttons.cn, function(c){
15343 c.cls += ' btn-' + _this.size;
15346 if (_this.disabled) {
15353 style : 'display: contents',
15358 cls: 'form-hidden-field'
15362 cls: 'roo-select2-choices',
15366 cls: 'roo-select2-search-field',
15377 cls: 'roo-select2-container input-group roo-select2-container-multi',
15383 // cls: 'typeahead typeahead-long dropdown-menu',
15384 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15389 if(this.hasFeedback && !this.allowBlank){
15393 cls: 'glyphicon form-control-feedback'
15396 combobox.cn.push(feedback);
15403 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15404 tooltip : 'This field is required'
15406 if (Roo.bootstrap.version == 4) {
15409 style : 'display:none'
15412 if (align ==='left' && this.fieldLabel.length) {
15414 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15421 cls : 'control-label col-form-label',
15422 html : this.fieldLabel
15434 var labelCfg = cfg.cn[1];
15435 var contentCfg = cfg.cn[2];
15438 if(this.indicatorpos == 'right'){
15444 cls : 'control-label col-form-label',
15448 html : this.fieldLabel
15464 labelCfg = cfg.cn[0];
15465 contentCfg = cfg.cn[1];
15469 if(this.labelWidth > 12){
15470 labelCfg.style = "width: " + this.labelWidth + 'px';
15472 if(this.width * 1 > 0){
15473 contentCfg.style = "width: " + this.width + 'px';
15475 if(this.labelWidth < 13 && this.labelmd == 0){
15476 this.labelmd = this.labelWidth;
15479 if(this.labellg > 0){
15480 labelCfg.cls += ' col-lg-' + this.labellg;
15481 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15484 if(this.labelmd > 0){
15485 labelCfg.cls += ' col-md-' + this.labelmd;
15486 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15489 if(this.labelsm > 0){
15490 labelCfg.cls += ' col-sm-' + this.labelsm;
15491 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15494 if(this.labelxs > 0){
15495 labelCfg.cls += ' col-xs-' + this.labelxs;
15496 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15500 } else if ( this.fieldLabel.length) {
15501 // Roo.log(" label");
15506 //cls : 'input-group-addon',
15507 html : this.fieldLabel
15512 if(this.indicatorpos == 'right'){
15516 //cls : 'input-group-addon',
15517 html : this.fieldLabel
15527 // Roo.log(" no label && no align");
15534 ['xs','sm','md','lg'].map(function(size){
15535 if (settings[size]) {
15536 cfg.cls += ' col-' + size + '-' + settings[size];
15544 _initEventsCalled : false,
15547 initEvents: function()
15549 if (this._initEventsCalled) { // as we call render... prevent looping...
15552 this._initEventsCalled = true;
15555 throw "can not find store for combo";
15558 this.indicator = this.indicatorEl();
15560 this.store = Roo.factory(this.store, Roo.data);
15561 this.store.parent = this;
15563 // if we are building from html. then this element is so complex, that we can not really
15564 // use the rendered HTML.
15565 // so we have to trash and replace the previous code.
15566 if (Roo.XComponent.build_from_html) {
15567 // remove this element....
15568 var e = this.el.dom, k=0;
15569 while (e ) { e = e.previousSibling; ++k;}
15574 this.rendered = false;
15576 this.render(this.parent().getChildContainer(true), k);
15579 if(Roo.isIOS && this.useNativeIOS){
15580 this.initIOSView();
15588 if(Roo.isTouch && this.mobileTouchView){
15589 this.initTouchView();
15594 this.initTickableEvents();
15598 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15600 if(this.hiddenName){
15602 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15604 this.hiddenField.dom.value =
15605 this.hiddenValue !== undefined ? this.hiddenValue :
15606 this.value !== undefined ? this.value : '';
15608 // prevent input submission
15609 this.el.dom.removeAttribute('name');
15610 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15615 // this.el.dom.setAttribute('autocomplete', 'off');
15618 var cls = 'x-combo-list';
15620 //this.list = new Roo.Layer({
15621 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15627 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15628 _this.list.setWidth(lw);
15631 this.list.on('mouseover', this.onViewOver, this);
15632 this.list.on('mousemove', this.onViewMove, this);
15633 this.list.on('scroll', this.onViewScroll, this);
15636 this.list.swallowEvent('mousewheel');
15637 this.assetHeight = 0;
15640 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15641 this.assetHeight += this.header.getHeight();
15644 this.innerList = this.list.createChild({cls:cls+'-inner'});
15645 this.innerList.on('mouseover', this.onViewOver, this);
15646 this.innerList.on('mousemove', this.onViewMove, this);
15647 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15649 if(this.allowBlank && !this.pageSize && !this.disableClear){
15650 this.footer = this.list.createChild({cls:cls+'-ft'});
15651 this.pageTb = new Roo.Toolbar(this.footer);
15655 this.footer = this.list.createChild({cls:cls+'-ft'});
15656 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15657 {pageSize: this.pageSize});
15661 if (this.pageTb && this.allowBlank && !this.disableClear) {
15663 this.pageTb.add(new Roo.Toolbar.Fill(), {
15664 cls: 'x-btn-icon x-btn-clear',
15666 handler: function()
15669 _this.clearValue();
15670 _this.onSelect(false, -1);
15675 this.assetHeight += this.footer.getHeight();
15680 this.tpl = Roo.bootstrap.version == 4 ?
15681 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15682 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15685 this.view = new Roo.View(this.list, this.tpl, {
15686 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15688 //this.view.wrapEl.setDisplayed(false);
15689 this.view.on('click', this.onViewClick, this);
15692 this.store.on('beforeload', this.onBeforeLoad, this);
15693 this.store.on('load', this.onLoad, this);
15694 this.store.on('loadexception', this.onLoadException, this);
15696 if(this.resizable){
15697 this.resizer = new Roo.Resizable(this.list, {
15698 pinned:true, handles:'se'
15700 this.resizer.on('resize', function(r, w, h){
15701 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15702 this.listWidth = w;
15703 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15704 this.restrictHeight();
15706 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15709 if(!this.editable){
15710 this.editable = true;
15711 this.setEditable(false);
15716 if (typeof(this.events.add.listeners) != 'undefined') {
15718 this.addicon = this.wrap.createChild(
15719 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15721 this.addicon.on('click', function(e) {
15722 this.fireEvent('add', this);
15725 if (typeof(this.events.edit.listeners) != 'undefined') {
15727 this.editicon = this.wrap.createChild(
15728 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15729 if (this.addicon) {
15730 this.editicon.setStyle('margin-left', '40px');
15732 this.editicon.on('click', function(e) {
15734 // we fire even if inothing is selected..
15735 this.fireEvent('edit', this, this.lastData );
15741 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15742 "up" : function(e){
15743 this.inKeyMode = true;
15747 "down" : function(e){
15748 if(!this.isExpanded()){
15749 this.onTriggerClick();
15751 this.inKeyMode = true;
15756 "enter" : function(e){
15757 // this.onViewClick();
15761 if(this.fireEvent("specialkey", this, e)){
15762 this.onViewClick(false);
15768 "esc" : function(e){
15772 "tab" : function(e){
15775 if(this.fireEvent("specialkey", this, e)){
15776 this.onViewClick(false);
15784 doRelay : function(foo, bar, hname){
15785 if(hname == 'down' || this.scope.isExpanded()){
15786 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15795 this.queryDelay = Math.max(this.queryDelay || 10,
15796 this.mode == 'local' ? 10 : 250);
15799 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15801 if(this.typeAhead){
15802 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15804 if(this.editable !== false){
15805 this.inputEl().on("keyup", this.onKeyUp, this);
15807 if(this.forceSelection){
15808 this.inputEl().on('blur', this.doForce, this);
15812 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15813 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15817 initTickableEvents: function()
15821 if(this.hiddenName){
15823 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15825 this.hiddenField.dom.value =
15826 this.hiddenValue !== undefined ? this.hiddenValue :
15827 this.value !== undefined ? this.value : '';
15829 // prevent input submission
15830 this.el.dom.removeAttribute('name');
15831 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15836 // this.list = this.el.select('ul.dropdown-menu',true).first();
15838 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15839 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15840 if(this.triggerList){
15841 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15844 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15845 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15847 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15848 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15850 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15851 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15853 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15854 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15855 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15858 this.cancelBtn.hide();
15863 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15864 _this.list.setWidth(lw);
15867 this.list.on('mouseover', this.onViewOver, this);
15868 this.list.on('mousemove', this.onViewMove, this);
15870 this.list.on('scroll', this.onViewScroll, this);
15873 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15874 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15877 this.view = new Roo.View(this.list, this.tpl, {
15882 selectedClass: this.selectedClass
15885 //this.view.wrapEl.setDisplayed(false);
15886 this.view.on('click', this.onViewClick, this);
15890 this.store.on('beforeload', this.onBeforeLoad, this);
15891 this.store.on('load', this.onLoad, this);
15892 this.store.on('loadexception', this.onLoadException, this);
15895 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15896 "up" : function(e){
15897 this.inKeyMode = true;
15901 "down" : function(e){
15902 this.inKeyMode = true;
15906 "enter" : function(e){
15907 if(this.fireEvent("specialkey", this, e)){
15908 this.onViewClick(false);
15914 "esc" : function(e){
15915 this.onTickableFooterButtonClick(e, false, false);
15918 "tab" : function(e){
15919 this.fireEvent("specialkey", this, e);
15921 this.onTickableFooterButtonClick(e, false, false);
15928 doRelay : function(e, fn, key){
15929 if(this.scope.isExpanded()){
15930 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15939 this.queryDelay = Math.max(this.queryDelay || 10,
15940 this.mode == 'local' ? 10 : 250);
15943 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15945 if(this.typeAhead){
15946 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15949 if(this.editable !== false){
15950 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15953 this.indicator = this.indicatorEl();
15955 if(this.indicator){
15956 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15957 this.indicator.hide();
15962 onDestroy : function(){
15964 this.view.setStore(null);
15965 this.view.el.removeAllListeners();
15966 this.view.el.remove();
15967 this.view.purgeListeners();
15970 this.list.dom.innerHTML = '';
15974 this.store.un('beforeload', this.onBeforeLoad, this);
15975 this.store.un('load', this.onLoad, this);
15976 this.store.un('loadexception', this.onLoadException, this);
15978 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15982 fireKey : function(e){
15983 if(e.isNavKeyPress() && !this.list.isVisible()){
15984 this.fireEvent("specialkey", this, e);
15989 onResize: function(w, h)
15993 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15995 // if(typeof w != 'number'){
15996 // // we do not handle it!?!?
15999 // var tw = this.trigger.getWidth();
16000 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16001 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16003 // this.inputEl().setWidth( this.adjustWidth('input', x));
16005 // //this.trigger.setStyle('left', x+'px');
16007 // if(this.list && this.listWidth === undefined){
16008 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16009 // this.list.setWidth(lw);
16010 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16018 * Allow or prevent the user from directly editing the field text. If false is passed,
16019 * the user will only be able to select from the items defined in the dropdown list. This method
16020 * is the runtime equivalent of setting the 'editable' config option at config time.
16021 * @param {Boolean} value True to allow the user to directly edit the field text
16023 setEditable : function(value){
16024 if(value == this.editable){
16027 this.editable = value;
16029 this.inputEl().dom.setAttribute('readOnly', true);
16030 this.inputEl().on('mousedown', this.onTriggerClick, this);
16031 this.inputEl().addClass('x-combo-noedit');
16033 this.inputEl().dom.setAttribute('readOnly', false);
16034 this.inputEl().un('mousedown', this.onTriggerClick, this);
16035 this.inputEl().removeClass('x-combo-noedit');
16041 onBeforeLoad : function(combo,opts){
16042 if(!this.hasFocus){
16046 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16048 this.restrictHeight();
16049 this.selectedIndex = -1;
16053 onLoad : function(){
16055 this.hasQuery = false;
16057 if(!this.hasFocus){
16061 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16062 this.loading.hide();
16065 if(this.store.getCount() > 0){
16068 this.restrictHeight();
16069 if(this.lastQuery == this.allQuery){
16070 if(this.editable && !this.tickable){
16071 this.inputEl().dom.select();
16075 !this.selectByValue(this.value, true) &&
16078 !this.store.lastOptions ||
16079 typeof(this.store.lastOptions.add) == 'undefined' ||
16080 this.store.lastOptions.add != true
16083 this.select(0, true);
16086 if(this.autoFocus){
16089 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16090 this.taTask.delay(this.typeAheadDelay);
16094 this.onEmptyResults();
16100 onLoadException : function()
16102 this.hasQuery = false;
16104 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16105 this.loading.hide();
16108 if(this.tickable && this.editable){
16113 // only causes errors at present
16114 //Roo.log(this.store.reader.jsonData);
16115 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16117 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16123 onTypeAhead : function(){
16124 if(this.store.getCount() > 0){
16125 var r = this.store.getAt(0);
16126 var newValue = r.data[this.displayField];
16127 var len = newValue.length;
16128 var selStart = this.getRawValue().length;
16130 if(selStart != len){
16131 this.setRawValue(newValue);
16132 this.selectText(selStart, newValue.length);
16138 onSelect : function(record, index){
16140 if(this.fireEvent('beforeselect', this, record, index) !== false){
16142 this.setFromData(index > -1 ? record.data : false);
16145 this.fireEvent('select', this, record, index);
16150 * Returns the currently selected field value or empty string if no value is set.
16151 * @return {String} value The selected value
16153 getValue : function()
16155 if(Roo.isIOS && this.useNativeIOS){
16156 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16160 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16163 if(this.valueField){
16164 return typeof this.value != 'undefined' ? this.value : '';
16166 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16170 getRawValue : function()
16172 if(Roo.isIOS && this.useNativeIOS){
16173 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16176 var v = this.inputEl().getValue();
16182 * Clears any text/value currently set in the field
16184 clearValue : function(){
16186 if(this.hiddenField){
16187 this.hiddenField.dom.value = '';
16190 this.setRawValue('');
16191 this.lastSelectionText = '';
16192 this.lastData = false;
16194 var close = this.closeTriggerEl();
16205 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16206 * will be displayed in the field. If the value does not match the data value of an existing item,
16207 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16208 * Otherwise the field will be blank (although the value will still be set).
16209 * @param {String} value The value to match
16211 setValue : function(v)
16213 if(Roo.isIOS && this.useNativeIOS){
16214 this.setIOSValue(v);
16224 if(this.valueField){
16225 var r = this.findRecord(this.valueField, v);
16227 text = r.data[this.displayField];
16228 }else if(this.valueNotFoundText !== undefined){
16229 text = this.valueNotFoundText;
16232 this.lastSelectionText = text;
16233 if(this.hiddenField){
16234 this.hiddenField.dom.value = v;
16236 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16239 var close = this.closeTriggerEl();
16242 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16248 * @property {Object} the last set data for the element
16253 * Sets the value of the field based on a object which is related to the record format for the store.
16254 * @param {Object} value the value to set as. or false on reset?
16256 setFromData : function(o){
16263 var dv = ''; // display value
16264 var vv = ''; // value value..
16266 if (this.displayField) {
16267 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16269 // this is an error condition!!!
16270 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16273 if(this.valueField){
16274 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16277 var close = this.closeTriggerEl();
16280 if(dv.length || vv * 1 > 0){
16282 this.blockFocus=true;
16288 if(this.hiddenField){
16289 this.hiddenField.dom.value = vv;
16291 this.lastSelectionText = dv;
16292 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16296 // no hidden field.. - we store the value in 'value', but still display
16297 // display field!!!!
16298 this.lastSelectionText = dv;
16299 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16306 reset : function(){
16307 // overridden so that last data is reset..
16314 this.setValue(this.originalValue);
16315 //this.clearInvalid();
16316 this.lastData = false;
16318 this.view.clearSelections();
16324 findRecord : function(prop, value){
16326 if(this.store.getCount() > 0){
16327 this.store.each(function(r){
16328 if(r.data[prop] == value){
16338 getName: function()
16340 // returns hidden if it's set..
16341 if (!this.rendered) {return ''};
16342 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16346 onViewMove : function(e, t){
16347 this.inKeyMode = false;
16351 onViewOver : function(e, t){
16352 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16355 var item = this.view.findItemFromChild(t);
16358 var index = this.view.indexOf(item);
16359 this.select(index, false);
16364 onViewClick : function(view, doFocus, el, e)
16366 var index = this.view.getSelectedIndexes()[0];
16368 var r = this.store.getAt(index);
16372 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16379 Roo.each(this.tickItems, function(v,k){
16381 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16383 _this.tickItems.splice(k, 1);
16385 if(typeof(e) == 'undefined' && view == false){
16386 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16398 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16399 this.tickItems.push(r.data);
16402 if(typeof(e) == 'undefined' && view == false){
16403 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16410 this.onSelect(r, index);
16412 if(doFocus !== false && !this.blockFocus){
16413 this.inputEl().focus();
16418 restrictHeight : function(){
16419 //this.innerList.dom.style.height = '';
16420 //var inner = this.innerList.dom;
16421 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16422 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16423 //this.list.beginUpdate();
16424 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16425 this.list.alignTo(this.inputEl(), this.listAlign);
16426 this.list.alignTo(this.inputEl(), this.listAlign);
16427 //this.list.endUpdate();
16431 onEmptyResults : function(){
16433 if(this.tickable && this.editable){
16434 this.hasFocus = false;
16435 this.restrictHeight();
16443 * Returns true if the dropdown list is expanded, else false.
16445 isExpanded : function(){
16446 return this.list.isVisible();
16450 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16451 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16452 * @param {String} value The data value of the item to select
16453 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16454 * selected item if it is not currently in view (defaults to true)
16455 * @return {Boolean} True if the value matched an item in the list, else false
16457 selectByValue : function(v, scrollIntoView){
16458 if(v !== undefined && v !== null){
16459 var r = this.findRecord(this.valueField || this.displayField, v);
16461 this.select(this.store.indexOf(r), scrollIntoView);
16469 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16470 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16471 * @param {Number} index The zero-based index of the list item to select
16472 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16473 * selected item if it is not currently in view (defaults to true)
16475 select : function(index, scrollIntoView){
16476 this.selectedIndex = index;
16477 this.view.select(index);
16478 if(scrollIntoView !== false){
16479 var el = this.view.getNode(index);
16481 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16484 this.list.scrollChildIntoView(el, false);
16490 selectNext : function(){
16491 var ct = this.store.getCount();
16493 if(this.selectedIndex == -1){
16495 }else if(this.selectedIndex < ct-1){
16496 this.select(this.selectedIndex+1);
16502 selectPrev : function(){
16503 var ct = this.store.getCount();
16505 if(this.selectedIndex == -1){
16507 }else if(this.selectedIndex != 0){
16508 this.select(this.selectedIndex-1);
16514 onKeyUp : function(e){
16515 if(this.editable !== false && !e.isSpecialKey()){
16516 this.lastKey = e.getKey();
16517 this.dqTask.delay(this.queryDelay);
16522 validateBlur : function(){
16523 return !this.list || !this.list.isVisible();
16527 initQuery : function(){
16529 var v = this.getRawValue();
16531 if(this.tickable && this.editable){
16532 v = this.tickableInputEl().getValue();
16539 doForce : function(){
16540 if(this.inputEl().dom.value.length > 0){
16541 this.inputEl().dom.value =
16542 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16548 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16549 * query allowing the query action to be canceled if needed.
16550 * @param {String} query The SQL query to execute
16551 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16552 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16553 * saved in the current store (defaults to false)
16555 doQuery : function(q, forceAll){
16557 if(q === undefined || q === null){
16562 forceAll: forceAll,
16566 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16571 forceAll = qe.forceAll;
16572 if(forceAll === true || (q.length >= this.minChars)){
16574 this.hasQuery = true;
16576 if(this.lastQuery != q || this.alwaysQuery){
16577 this.lastQuery = q;
16578 if(this.mode == 'local'){
16579 this.selectedIndex = -1;
16581 this.store.clearFilter();
16584 if(this.specialFilter){
16585 this.fireEvent('specialfilter', this);
16590 this.store.filter(this.displayField, q);
16593 this.store.fireEvent("datachanged", this.store);
16600 this.store.baseParams[this.queryParam] = q;
16602 var options = {params : this.getParams(q)};
16605 options.add = true;
16606 options.params.start = this.page * this.pageSize;
16609 this.store.load(options);
16612 * this code will make the page width larger, at the beginning, the list not align correctly,
16613 * we should expand the list on onLoad
16614 * so command out it
16619 this.selectedIndex = -1;
16624 this.loadNext = false;
16628 getParams : function(q){
16630 //p[this.queryParam] = q;
16634 p.limit = this.pageSize;
16640 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16642 collapse : function(){
16643 if(!this.isExpanded()){
16649 this.hasFocus = false;
16653 this.cancelBtn.hide();
16654 this.trigger.show();
16657 this.tickableInputEl().dom.value = '';
16658 this.tickableInputEl().blur();
16663 Roo.get(document).un('mousedown', this.collapseIf, this);
16664 Roo.get(document).un('mousewheel', this.collapseIf, this);
16665 if (!this.editable) {
16666 Roo.get(document).un('keydown', this.listKeyPress, this);
16668 this.fireEvent('collapse', this);
16674 collapseIf : function(e){
16675 var in_combo = e.within(this.el);
16676 var in_list = e.within(this.list);
16677 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16679 if (in_combo || in_list || is_list) {
16680 //e.stopPropagation();
16685 this.onTickableFooterButtonClick(e, false, false);
16693 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16695 expand : function(){
16697 if(this.isExpanded() || !this.hasFocus){
16701 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16702 this.list.setWidth(lw);
16708 this.restrictHeight();
16712 this.tickItems = Roo.apply([], this.item);
16715 this.cancelBtn.show();
16716 this.trigger.hide();
16719 this.tickableInputEl().focus();
16724 Roo.get(document).on('mousedown', this.collapseIf, this);
16725 Roo.get(document).on('mousewheel', this.collapseIf, this);
16726 if (!this.editable) {
16727 Roo.get(document).on('keydown', this.listKeyPress, this);
16730 this.fireEvent('expand', this);
16734 // Implements the default empty TriggerField.onTriggerClick function
16735 onTriggerClick : function(e)
16737 Roo.log('trigger click');
16739 if(this.disabled || !this.triggerList){
16744 this.loadNext = false;
16746 if(this.isExpanded()){
16748 if (!this.blockFocus) {
16749 this.inputEl().focus();
16753 this.hasFocus = true;
16754 if(this.triggerAction == 'all') {
16755 this.doQuery(this.allQuery, true);
16757 this.doQuery(this.getRawValue());
16759 if (!this.blockFocus) {
16760 this.inputEl().focus();
16765 onTickableTriggerClick : function(e)
16772 this.loadNext = false;
16773 this.hasFocus = true;
16775 if(this.triggerAction == 'all') {
16776 this.doQuery(this.allQuery, true);
16778 this.doQuery(this.getRawValue());
16782 onSearchFieldClick : function(e)
16784 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16785 this.onTickableFooterButtonClick(e, false, false);
16789 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16794 this.loadNext = false;
16795 this.hasFocus = true;
16797 if(this.triggerAction == 'all') {
16798 this.doQuery(this.allQuery, true);
16800 this.doQuery(this.getRawValue());
16804 listKeyPress : function(e)
16806 //Roo.log('listkeypress');
16807 // scroll to first matching element based on key pres..
16808 if (e.isSpecialKey()) {
16811 var k = String.fromCharCode(e.getKey()).toUpperCase();
16814 var csel = this.view.getSelectedNodes();
16815 var cselitem = false;
16817 var ix = this.view.indexOf(csel[0]);
16818 cselitem = this.store.getAt(ix);
16819 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16825 this.store.each(function(v) {
16827 // start at existing selection.
16828 if (cselitem.id == v.id) {
16834 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16835 match = this.store.indexOf(v);
16841 if (match === false) {
16842 return true; // no more action?
16845 this.view.select(match);
16846 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16847 sn.scrollIntoView(sn.dom.parentNode, false);
16850 onViewScroll : function(e, t){
16852 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){
16856 this.hasQuery = true;
16858 this.loading = this.list.select('.loading', true).first();
16860 if(this.loading === null){
16861 this.list.createChild({
16863 cls: 'loading roo-select2-more-results roo-select2-active',
16864 html: 'Loading more results...'
16867 this.loading = this.list.select('.loading', true).first();
16869 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16871 this.loading.hide();
16874 this.loading.show();
16879 this.loadNext = true;
16881 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16886 addItem : function(o)
16888 var dv = ''; // display value
16890 if (this.displayField) {
16891 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16893 // this is an error condition!!!
16894 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16901 var choice = this.choices.createChild({
16903 cls: 'roo-select2-search-choice',
16912 cls: 'roo-select2-search-choice-close fa fa-times',
16917 }, this.searchField);
16919 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16921 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16929 this.inputEl().dom.value = '';
16934 onRemoveItem : function(e, _self, o)
16936 e.preventDefault();
16938 this.lastItem = Roo.apply([], this.item);
16940 var index = this.item.indexOf(o.data) * 1;
16943 Roo.log('not this item?!');
16947 this.item.splice(index, 1);
16952 this.fireEvent('remove', this, e);
16958 syncValue : function()
16960 if(!this.item.length){
16967 Roo.each(this.item, function(i){
16968 if(_this.valueField){
16969 value.push(i[_this.valueField]);
16976 this.value = value.join(',');
16978 if(this.hiddenField){
16979 this.hiddenField.dom.value = this.value;
16982 this.store.fireEvent("datachanged", this.store);
16987 clearItem : function()
16989 if(!this.multiple){
16995 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17003 if(this.tickable && !Roo.isTouch){
17004 this.view.refresh();
17008 inputEl: function ()
17010 if(Roo.isIOS && this.useNativeIOS){
17011 return this.el.select('select.roo-ios-select', true).first();
17014 if(Roo.isTouch && this.mobileTouchView){
17015 return this.el.select('input.form-control',true).first();
17019 return this.searchField;
17022 return this.el.select('input.form-control',true).first();
17025 onTickableFooterButtonClick : function(e, btn, el)
17027 e.preventDefault();
17029 this.lastItem = Roo.apply([], this.item);
17031 if(btn && btn.name == 'cancel'){
17032 this.tickItems = Roo.apply([], this.item);
17041 Roo.each(this.tickItems, function(o){
17049 validate : function()
17051 if(this.getVisibilityEl().hasClass('hidden')){
17055 var v = this.getRawValue();
17058 v = this.getValue();
17061 if(this.disabled || this.allowBlank || v.length){
17066 this.markInvalid();
17070 tickableInputEl : function()
17072 if(!this.tickable || !this.editable){
17073 return this.inputEl();
17076 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17080 getAutoCreateTouchView : function()
17085 cls: 'form-group' //input-group
17091 type : this.inputType,
17092 cls : 'form-control x-combo-noedit',
17093 autocomplete: 'new-password',
17094 placeholder : this.placeholder || '',
17099 input.name = this.name;
17103 input.cls += ' input-' + this.size;
17106 if (this.disabled) {
17107 input.disabled = true;
17111 cls : 'roo-combobox-wrap',
17118 inputblock.cls += ' input-group';
17120 inputblock.cn.unshift({
17122 cls : 'input-group-addon input-group-prepend input-group-text',
17127 if(this.removable && !this.multiple){
17128 inputblock.cls += ' roo-removable';
17130 inputblock.cn.push({
17133 cls : 'roo-combo-removable-btn close'
17137 if(this.hasFeedback && !this.allowBlank){
17139 inputblock.cls += ' has-feedback';
17141 inputblock.cn.push({
17143 cls: 'glyphicon form-control-feedback'
17150 inputblock.cls += (this.before) ? '' : ' input-group';
17152 inputblock.cn.push({
17154 cls : 'input-group-addon input-group-append input-group-text',
17160 var ibwrap = inputblock;
17165 cls: 'roo-select2-choices',
17169 cls: 'roo-select2-search-field',
17182 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17187 cls: 'form-hidden-field'
17193 if(!this.multiple && this.showToggleBtn){
17199 if (this.caret != false) {
17202 cls: 'fa fa-' + this.caret
17209 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17211 Roo.bootstrap.version == 3 ? caret : '',
17214 cls: 'combobox-clear',
17228 combobox.cls += ' roo-select2-container-multi';
17231 var align = this.labelAlign || this.parentLabelAlign();
17233 if (align ==='left' && this.fieldLabel.length) {
17238 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17239 tooltip : 'This field is required'
17243 cls : 'control-label col-form-label',
17244 html : this.fieldLabel
17248 cls : 'roo-combobox-wrap ',
17255 var labelCfg = cfg.cn[1];
17256 var contentCfg = cfg.cn[2];
17259 if(this.indicatorpos == 'right'){
17264 cls : 'control-label col-form-label',
17268 html : this.fieldLabel
17272 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17273 tooltip : 'This field is required'
17278 cls : "roo-combobox-wrap ",
17286 labelCfg = cfg.cn[0];
17287 contentCfg = cfg.cn[1];
17292 if(this.labelWidth > 12){
17293 labelCfg.style = "width: " + this.labelWidth + 'px';
17296 if(this.labelWidth < 13 && this.labelmd == 0){
17297 this.labelmd = this.labelWidth;
17300 if(this.labellg > 0){
17301 labelCfg.cls += ' col-lg-' + this.labellg;
17302 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17305 if(this.labelmd > 0){
17306 labelCfg.cls += ' col-md-' + this.labelmd;
17307 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17310 if(this.labelsm > 0){
17311 labelCfg.cls += ' col-sm-' + this.labelsm;
17312 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17315 if(this.labelxs > 0){
17316 labelCfg.cls += ' col-xs-' + this.labelxs;
17317 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17321 } else if ( this.fieldLabel.length) {
17325 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17326 tooltip : 'This field is required'
17330 cls : 'control-label',
17331 html : this.fieldLabel
17342 if(this.indicatorpos == 'right'){
17346 cls : 'control-label',
17347 html : this.fieldLabel,
17351 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17352 tooltip : 'This field is required'
17369 var settings = this;
17371 ['xs','sm','md','lg'].map(function(size){
17372 if (settings[size]) {
17373 cfg.cls += ' col-' + size + '-' + settings[size];
17380 initTouchView : function()
17382 this.renderTouchView();
17384 this.touchViewEl.on('scroll', function(){
17385 this.el.dom.scrollTop = 0;
17388 this.originalValue = this.getValue();
17390 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17392 this.inputEl().on("click", this.showTouchView, this);
17393 if (this.triggerEl) {
17394 this.triggerEl.on("click", this.showTouchView, this);
17398 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17399 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17401 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17403 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17404 this.store.on('load', this.onTouchViewLoad, this);
17405 this.store.on('loadexception', this.onTouchViewLoadException, this);
17407 if(this.hiddenName){
17409 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17411 this.hiddenField.dom.value =
17412 this.hiddenValue !== undefined ? this.hiddenValue :
17413 this.value !== undefined ? this.value : '';
17415 this.el.dom.removeAttribute('name');
17416 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17420 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17421 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17424 if(this.removable && !this.multiple){
17425 var close = this.closeTriggerEl();
17427 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17428 close.on('click', this.removeBtnClick, this, close);
17432 * fix the bug in Safari iOS8
17434 this.inputEl().on("focus", function(e){
17435 document.activeElement.blur();
17438 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17445 renderTouchView : function()
17447 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17448 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17450 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17451 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17453 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17454 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17455 this.touchViewBodyEl.setStyle('overflow', 'auto');
17457 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17458 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17460 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17461 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17465 showTouchView : function()
17471 this.touchViewHeaderEl.hide();
17473 if(this.modalTitle.length){
17474 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17475 this.touchViewHeaderEl.show();
17478 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17479 this.touchViewEl.show();
17481 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17483 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17484 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17486 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17488 if(this.modalTitle.length){
17489 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17492 this.touchViewBodyEl.setHeight(bodyHeight);
17496 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17498 this.touchViewEl.addClass(['in','show']);
17501 if(this._touchViewMask){
17502 Roo.get(document.body).addClass("x-body-masked");
17503 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17504 this._touchViewMask.setStyle('z-index', 10000);
17505 this._touchViewMask.addClass('show');
17508 this.doTouchViewQuery();
17512 hideTouchView : function()
17514 this.touchViewEl.removeClass(['in','show']);
17518 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17520 this.touchViewEl.setStyle('display', 'none');
17523 if(this._touchViewMask){
17524 this._touchViewMask.removeClass('show');
17525 Roo.get(document.body).removeClass("x-body-masked");
17529 setTouchViewValue : function()
17536 Roo.each(this.tickItems, function(o){
17541 this.hideTouchView();
17544 doTouchViewQuery : function()
17553 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17557 if(!this.alwaysQuery || this.mode == 'local'){
17558 this.onTouchViewLoad();
17565 onTouchViewBeforeLoad : function(combo,opts)
17571 onTouchViewLoad : function()
17573 if(this.store.getCount() < 1){
17574 this.onTouchViewEmptyResults();
17578 this.clearTouchView();
17580 var rawValue = this.getRawValue();
17582 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17584 this.tickItems = [];
17586 this.store.data.each(function(d, rowIndex){
17587 var row = this.touchViewListGroup.createChild(template);
17589 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17590 row.addClass(d.data.cls);
17593 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17596 html : d.data[this.displayField]
17599 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17600 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17603 row.removeClass('selected');
17604 if(!this.multiple && this.valueField &&
17605 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17608 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17609 row.addClass('selected');
17612 if(this.multiple && this.valueField &&
17613 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17617 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17618 this.tickItems.push(d.data);
17621 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17625 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17627 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17629 if(this.modalTitle.length){
17630 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17633 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17635 if(this.mobile_restrict_height && listHeight < bodyHeight){
17636 this.touchViewBodyEl.setHeight(listHeight);
17641 if(firstChecked && listHeight > bodyHeight){
17642 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17647 onTouchViewLoadException : function()
17649 this.hideTouchView();
17652 onTouchViewEmptyResults : function()
17654 this.clearTouchView();
17656 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17658 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17662 clearTouchView : function()
17664 this.touchViewListGroup.dom.innerHTML = '';
17667 onTouchViewClick : function(e, el, o)
17669 e.preventDefault();
17672 var rowIndex = o.rowIndex;
17674 var r = this.store.getAt(rowIndex);
17676 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17678 if(!this.multiple){
17679 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17680 c.dom.removeAttribute('checked');
17683 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17685 this.setFromData(r.data);
17687 var close = this.closeTriggerEl();
17693 this.hideTouchView();
17695 this.fireEvent('select', this, r, rowIndex);
17700 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17701 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17702 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17706 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17707 this.addItem(r.data);
17708 this.tickItems.push(r.data);
17712 getAutoCreateNativeIOS : function()
17715 cls: 'form-group' //input-group,
17720 cls : 'roo-ios-select'
17724 combobox.name = this.name;
17727 if (this.disabled) {
17728 combobox.disabled = true;
17731 var settings = this;
17733 ['xs','sm','md','lg'].map(function(size){
17734 if (settings[size]) {
17735 cfg.cls += ' col-' + size + '-' + settings[size];
17745 initIOSView : function()
17747 this.store.on('load', this.onIOSViewLoad, this);
17752 onIOSViewLoad : function()
17754 if(this.store.getCount() < 1){
17758 this.clearIOSView();
17760 if(this.allowBlank) {
17762 var default_text = '-- SELECT --';
17764 if(this.placeholder.length){
17765 default_text = this.placeholder;
17768 if(this.emptyTitle.length){
17769 default_text += ' - ' + this.emptyTitle + ' -';
17772 var opt = this.inputEl().createChild({
17775 html : default_text
17779 o[this.valueField] = 0;
17780 o[this.displayField] = default_text;
17782 this.ios_options.push({
17789 this.store.data.each(function(d, rowIndex){
17793 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17794 html = d.data[this.displayField];
17799 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17800 value = d.data[this.valueField];
17809 if(this.value == d.data[this.valueField]){
17810 option['selected'] = true;
17813 var opt = this.inputEl().createChild(option);
17815 this.ios_options.push({
17822 this.inputEl().on('change', function(){
17823 this.fireEvent('select', this);
17828 clearIOSView: function()
17830 this.inputEl().dom.innerHTML = '';
17832 this.ios_options = [];
17835 setIOSValue: function(v)
17839 if(!this.ios_options){
17843 Roo.each(this.ios_options, function(opts){
17845 opts.el.dom.removeAttribute('selected');
17847 if(opts.data[this.valueField] != v){
17851 opts.el.dom.setAttribute('selected', true);
17857 * @cfg {Boolean} grow
17861 * @cfg {Number} growMin
17865 * @cfg {Number} growMax
17874 Roo.apply(Roo.bootstrap.ComboBox, {
17878 cls: 'modal-header',
17900 cls: 'list-group-item',
17904 cls: 'roo-combobox-list-group-item-value'
17908 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17922 listItemCheckbox : {
17924 cls: 'list-group-item',
17928 cls: 'roo-combobox-list-group-item-value'
17932 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17948 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17953 cls: 'modal-footer',
17961 cls: 'col-xs-6 text-left',
17964 cls: 'btn btn-danger roo-touch-view-cancel',
17970 cls: 'col-xs-6 text-right',
17973 cls: 'btn btn-success roo-touch-view-ok',
17984 Roo.apply(Roo.bootstrap.ComboBox, {
17986 touchViewTemplate : {
17988 cls: 'modal fade roo-combobox-touch-view',
17992 cls: 'modal-dialog',
17993 style : 'position:fixed', // we have to fix position....
17997 cls: 'modal-content',
17999 Roo.bootstrap.ComboBox.header,
18000 Roo.bootstrap.ComboBox.body,
18001 Roo.bootstrap.ComboBox.footer
18010 * Ext JS Library 1.1.1
18011 * Copyright(c) 2006-2007, Ext JS, LLC.
18013 * Originally Released Under LGPL - original licence link has changed is not relivant.
18016 * <script type="text/javascript">
18021 * @extends Roo.util.Observable
18022 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18023 * This class also supports single and multi selection modes. <br>
18024 * Create a data model bound view:
18026 var store = new Roo.data.Store(...);
18028 var view = new Roo.View({
18030 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18032 singleSelect: true,
18033 selectedClass: "ydataview-selected",
18037 // listen for node click?
18038 view.on("click", function(vw, index, node, e){
18039 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18043 dataModel.load("foobar.xml");
18045 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18047 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18048 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18050 * Note: old style constructor is still suported (container, template, config)
18053 * Create a new View
18054 * @param {Object} config The config object
18057 Roo.View = function(config, depreciated_tpl, depreciated_config){
18059 this.parent = false;
18061 if (typeof(depreciated_tpl) == 'undefined') {
18062 // new way.. - universal constructor.
18063 Roo.apply(this, config);
18064 this.el = Roo.get(this.el);
18067 this.el = Roo.get(config);
18068 this.tpl = depreciated_tpl;
18069 Roo.apply(this, depreciated_config);
18071 this.wrapEl = this.el.wrap().wrap();
18072 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18075 if(typeof(this.tpl) == "string"){
18076 this.tpl = new Roo.Template(this.tpl);
18078 // support xtype ctors..
18079 this.tpl = new Roo.factory(this.tpl, Roo);
18083 this.tpl.compile();
18088 * @event beforeclick
18089 * Fires before a click is processed. Returns false to cancel the default action.
18090 * @param {Roo.View} this
18091 * @param {Number} index The index of the target node
18092 * @param {HTMLElement} node The target node
18093 * @param {Roo.EventObject} e The raw event object
18095 "beforeclick" : true,
18098 * Fires when a template node is clicked.
18099 * @param {Roo.View} this
18100 * @param {Number} index The index of the target node
18101 * @param {HTMLElement} node The target node
18102 * @param {Roo.EventObject} e The raw event object
18107 * Fires when a template node is double clicked.
18108 * @param {Roo.View} this
18109 * @param {Number} index The index of the target node
18110 * @param {HTMLElement} node The target node
18111 * @param {Roo.EventObject} e The raw event object
18115 * @event contextmenu
18116 * Fires when a template node is right clicked.
18117 * @param {Roo.View} this
18118 * @param {Number} index The index of the target node
18119 * @param {HTMLElement} node The target node
18120 * @param {Roo.EventObject} e The raw event object
18122 "contextmenu" : true,
18124 * @event selectionchange
18125 * Fires when the selected nodes change.
18126 * @param {Roo.View} this
18127 * @param {Array} selections Array of the selected nodes
18129 "selectionchange" : true,
18132 * @event beforeselect
18133 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18134 * @param {Roo.View} this
18135 * @param {HTMLElement} node The node to be selected
18136 * @param {Array} selections Array of currently selected nodes
18138 "beforeselect" : true,
18140 * @event preparedata
18141 * Fires on every row to render, to allow you to change the data.
18142 * @param {Roo.View} this
18143 * @param {Object} data to be rendered (change this)
18145 "preparedata" : true
18153 "click": this.onClick,
18154 "dblclick": this.onDblClick,
18155 "contextmenu": this.onContextMenu,
18159 this.selections = [];
18161 this.cmp = new Roo.CompositeElementLite([]);
18163 this.store = Roo.factory(this.store, Roo.data);
18164 this.setStore(this.store, true);
18167 if ( this.footer && this.footer.xtype) {
18169 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18171 this.footer.dataSource = this.store;
18172 this.footer.container = fctr;
18173 this.footer = Roo.factory(this.footer, Roo);
18174 fctr.insertFirst(this.el);
18176 // this is a bit insane - as the paging toolbar seems to detach the el..
18177 // dom.parentNode.parentNode.parentNode
18178 // they get detached?
18182 Roo.View.superclass.constructor.call(this);
18187 Roo.extend(Roo.View, Roo.util.Observable, {
18190 * @cfg {Roo.data.Store} store Data store to load data from.
18195 * @cfg {String|Roo.Element} el The container element.
18200 * @cfg {String|Roo.Template} tpl The template used by this View
18204 * @cfg {String} dataName the named area of the template to use as the data area
18205 * Works with domtemplates roo-name="name"
18209 * @cfg {String} selectedClass The css class to add to selected nodes
18211 selectedClass : "x-view-selected",
18213 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18218 * @cfg {String} text to display on mask (default Loading)
18222 * @cfg {Boolean} multiSelect Allow multiple selection
18224 multiSelect : false,
18226 * @cfg {Boolean} singleSelect Allow single selection
18228 singleSelect: false,
18231 * @cfg {Boolean} toggleSelect - selecting
18233 toggleSelect : false,
18236 * @cfg {Boolean} tickable - selecting
18241 * Returns the element this view is bound to.
18242 * @return {Roo.Element}
18244 getEl : function(){
18245 return this.wrapEl;
18251 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18253 refresh : function(){
18254 //Roo.log('refresh');
18257 // if we are using something like 'domtemplate', then
18258 // the what gets used is:
18259 // t.applySubtemplate(NAME, data, wrapping data..)
18260 // the outer template then get' applied with
18261 // the store 'extra data'
18262 // and the body get's added to the
18263 // roo-name="data" node?
18264 // <span class='roo-tpl-{name}'></span> ?????
18268 this.clearSelections();
18269 this.el.update("");
18271 var records = this.store.getRange();
18272 if(records.length < 1) {
18274 // is this valid?? = should it render a template??
18276 this.el.update(this.emptyText);
18280 if (this.dataName) {
18281 this.el.update(t.apply(this.store.meta)); //????
18282 el = this.el.child('.roo-tpl-' + this.dataName);
18285 for(var i = 0, len = records.length; i < len; i++){
18286 var data = this.prepareData(records[i].data, i, records[i]);
18287 this.fireEvent("preparedata", this, data, i, records[i]);
18289 var d = Roo.apply({}, data);
18292 Roo.apply(d, {'roo-id' : Roo.id()});
18296 Roo.each(this.parent.item, function(item){
18297 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18300 Roo.apply(d, {'roo-data-checked' : 'checked'});
18304 html[html.length] = Roo.util.Format.trim(
18306 t.applySubtemplate(this.dataName, d, this.store.meta) :
18313 el.update(html.join(""));
18314 this.nodes = el.dom.childNodes;
18315 this.updateIndexes(0);
18320 * Function to override to reformat the data that is sent to
18321 * the template for each node.
18322 * DEPRICATED - use the preparedata event handler.
18323 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18324 * a JSON object for an UpdateManager bound view).
18326 prepareData : function(data, index, record)
18328 this.fireEvent("preparedata", this, data, index, record);
18332 onUpdate : function(ds, record){
18333 // Roo.log('on update');
18334 this.clearSelections();
18335 var index = this.store.indexOf(record);
18336 var n = this.nodes[index];
18337 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18338 n.parentNode.removeChild(n);
18339 this.updateIndexes(index, index);
18345 onAdd : function(ds, records, index)
18347 //Roo.log(['on Add', ds, records, index] );
18348 this.clearSelections();
18349 if(this.nodes.length == 0){
18353 var n = this.nodes[index];
18354 for(var i = 0, len = records.length; i < len; i++){
18355 var d = this.prepareData(records[i].data, i, records[i]);
18357 this.tpl.insertBefore(n, d);
18360 this.tpl.append(this.el, d);
18363 this.updateIndexes(index);
18366 onRemove : function(ds, record, index){
18367 // Roo.log('onRemove');
18368 this.clearSelections();
18369 var el = this.dataName ?
18370 this.el.child('.roo-tpl-' + this.dataName) :
18373 el.dom.removeChild(this.nodes[index]);
18374 this.updateIndexes(index);
18378 * Refresh an individual node.
18379 * @param {Number} index
18381 refreshNode : function(index){
18382 this.onUpdate(this.store, this.store.getAt(index));
18385 updateIndexes : function(startIndex, endIndex){
18386 var ns = this.nodes;
18387 startIndex = startIndex || 0;
18388 endIndex = endIndex || ns.length - 1;
18389 for(var i = startIndex; i <= endIndex; i++){
18390 ns[i].nodeIndex = i;
18395 * Changes the data store this view uses and refresh the view.
18396 * @param {Store} store
18398 setStore : function(store, initial){
18399 if(!initial && this.store){
18400 this.store.un("datachanged", this.refresh);
18401 this.store.un("add", this.onAdd);
18402 this.store.un("remove", this.onRemove);
18403 this.store.un("update", this.onUpdate);
18404 this.store.un("clear", this.refresh);
18405 this.store.un("beforeload", this.onBeforeLoad);
18406 this.store.un("load", this.onLoad);
18407 this.store.un("loadexception", this.onLoad);
18411 store.on("datachanged", this.refresh, this);
18412 store.on("add", this.onAdd, this);
18413 store.on("remove", this.onRemove, this);
18414 store.on("update", this.onUpdate, this);
18415 store.on("clear", this.refresh, this);
18416 store.on("beforeload", this.onBeforeLoad, this);
18417 store.on("load", this.onLoad, this);
18418 store.on("loadexception", this.onLoad, this);
18426 * onbeforeLoad - masks the loading area.
18429 onBeforeLoad : function(store,opts)
18431 //Roo.log('onBeforeLoad');
18433 this.el.update("");
18435 this.el.mask(this.mask ? this.mask : "Loading" );
18437 onLoad : function ()
18444 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18445 * @param {HTMLElement} node
18446 * @return {HTMLElement} The template node
18448 findItemFromChild : function(node){
18449 var el = this.dataName ?
18450 this.el.child('.roo-tpl-' + this.dataName,true) :
18453 if(!node || node.parentNode == el){
18456 var p = node.parentNode;
18457 while(p && p != el){
18458 if(p.parentNode == el){
18467 onClick : function(e){
18468 var item = this.findItemFromChild(e.getTarget());
18470 var index = this.indexOf(item);
18471 if(this.onItemClick(item, index, e) !== false){
18472 this.fireEvent("click", this, index, item, e);
18475 this.clearSelections();
18480 onContextMenu : function(e){
18481 var item = this.findItemFromChild(e.getTarget());
18483 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18488 onDblClick : function(e){
18489 var item = this.findItemFromChild(e.getTarget());
18491 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18495 onItemClick : function(item, index, e)
18497 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18500 if (this.toggleSelect) {
18501 var m = this.isSelected(item) ? 'unselect' : 'select';
18504 _t[m](item, true, false);
18507 if(this.multiSelect || this.singleSelect){
18508 if(this.multiSelect && e.shiftKey && this.lastSelection){
18509 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18511 this.select(item, this.multiSelect && e.ctrlKey);
18512 this.lastSelection = item;
18515 if(!this.tickable){
18516 e.preventDefault();
18524 * Get the number of selected nodes.
18527 getSelectionCount : function(){
18528 return this.selections.length;
18532 * Get the currently selected nodes.
18533 * @return {Array} An array of HTMLElements
18535 getSelectedNodes : function(){
18536 return this.selections;
18540 * Get the indexes of the selected nodes.
18543 getSelectedIndexes : function(){
18544 var indexes = [], s = this.selections;
18545 for(var i = 0, len = s.length; i < len; i++){
18546 indexes.push(s[i].nodeIndex);
18552 * Clear all selections
18553 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18555 clearSelections : function(suppressEvent){
18556 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18557 this.cmp.elements = this.selections;
18558 this.cmp.removeClass(this.selectedClass);
18559 this.selections = [];
18560 if(!suppressEvent){
18561 this.fireEvent("selectionchange", this, this.selections);
18567 * Returns true if the passed node is selected
18568 * @param {HTMLElement/Number} node The node or node index
18569 * @return {Boolean}
18571 isSelected : function(node){
18572 var s = this.selections;
18576 node = this.getNode(node);
18577 return s.indexOf(node) !== -1;
18582 * @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
18583 * @param {Boolean} keepExisting (optional) true to keep existing selections
18584 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18586 select : function(nodeInfo, keepExisting, suppressEvent){
18587 if(nodeInfo instanceof Array){
18589 this.clearSelections(true);
18591 for(var i = 0, len = nodeInfo.length; i < len; i++){
18592 this.select(nodeInfo[i], true, true);
18596 var node = this.getNode(nodeInfo);
18597 if(!node || this.isSelected(node)){
18598 return; // already selected.
18601 this.clearSelections(true);
18604 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18605 Roo.fly(node).addClass(this.selectedClass);
18606 this.selections.push(node);
18607 if(!suppressEvent){
18608 this.fireEvent("selectionchange", this, this.selections);
18616 * @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
18617 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18618 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18620 unselect : function(nodeInfo, keepExisting, suppressEvent)
18622 if(nodeInfo instanceof Array){
18623 Roo.each(this.selections, function(s) {
18624 this.unselect(s, nodeInfo);
18628 var node = this.getNode(nodeInfo);
18629 if(!node || !this.isSelected(node)){
18630 //Roo.log("not selected");
18631 return; // not selected.
18635 Roo.each(this.selections, function(s) {
18637 Roo.fly(node).removeClass(this.selectedClass);
18644 this.selections= ns;
18645 this.fireEvent("selectionchange", this, this.selections);
18649 * Gets a template node.
18650 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18651 * @return {HTMLElement} The node or null if it wasn't found
18653 getNode : function(nodeInfo){
18654 if(typeof nodeInfo == "string"){
18655 return document.getElementById(nodeInfo);
18656 }else if(typeof nodeInfo == "number"){
18657 return this.nodes[nodeInfo];
18663 * Gets a range template nodes.
18664 * @param {Number} startIndex
18665 * @param {Number} endIndex
18666 * @return {Array} An array of nodes
18668 getNodes : function(start, end){
18669 var ns = this.nodes;
18670 start = start || 0;
18671 end = typeof end == "undefined" ? ns.length - 1 : end;
18674 for(var i = start; i <= end; i++){
18678 for(var i = start; i >= end; i--){
18686 * Finds the index of the passed node
18687 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18688 * @return {Number} The index of the node or -1
18690 indexOf : function(node){
18691 node = this.getNode(node);
18692 if(typeof node.nodeIndex == "number"){
18693 return node.nodeIndex;
18695 var ns = this.nodes;
18696 for(var i = 0, len = ns.length; i < len; i++){
18707 * based on jquery fullcalendar
18711 Roo.bootstrap = Roo.bootstrap || {};
18713 * @class Roo.bootstrap.Calendar
18714 * @extends Roo.bootstrap.Component
18715 * Bootstrap Calendar class
18716 * @cfg {Boolean} loadMask (true|false) default false
18717 * @cfg {Object} header generate the user specific header of the calendar, default false
18720 * Create a new Container
18721 * @param {Object} config The config object
18726 Roo.bootstrap.Calendar = function(config){
18727 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18731 * Fires when a date is selected
18732 * @param {DatePicker} this
18733 * @param {Date} date The selected date
18737 * @event monthchange
18738 * Fires when the displayed month changes
18739 * @param {DatePicker} this
18740 * @param {Date} date The selected month
18742 'monthchange': true,
18744 * @event evententer
18745 * Fires when mouse over an event
18746 * @param {Calendar} this
18747 * @param {event} Event
18749 'evententer': true,
18751 * @event eventleave
18752 * Fires when the mouse leaves an
18753 * @param {Calendar} this
18756 'eventleave': true,
18758 * @event eventclick
18759 * Fires when the mouse click an
18760 * @param {Calendar} this
18769 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18772 * @cfg {Number} startDay
18773 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18781 getAutoCreate : function(){
18784 var fc_button = function(name, corner, style, content ) {
18785 return Roo.apply({},{
18787 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18789 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18792 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18803 style : 'width:100%',
18810 cls : 'fc-header-left',
18812 fc_button('prev', 'left', 'arrow', '‹' ),
18813 fc_button('next', 'right', 'arrow', '›' ),
18814 { tag: 'span', cls: 'fc-header-space' },
18815 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18823 cls : 'fc-header-center',
18827 cls: 'fc-header-title',
18830 html : 'month / year'
18838 cls : 'fc-header-right',
18840 /* fc_button('month', 'left', '', 'month' ),
18841 fc_button('week', '', '', 'week' ),
18842 fc_button('day', 'right', '', 'day' )
18854 header = this.header;
18857 var cal_heads = function() {
18859 // fixme - handle this.
18861 for (var i =0; i < Date.dayNames.length; i++) {
18862 var d = Date.dayNames[i];
18865 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18866 html : d.substring(0,3)
18870 ret[0].cls += ' fc-first';
18871 ret[6].cls += ' fc-last';
18874 var cal_cell = function(n) {
18877 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18882 cls: 'fc-day-number',
18886 cls: 'fc-day-content',
18890 style: 'position: relative;' // height: 17px;
18902 var cal_rows = function() {
18905 for (var r = 0; r < 6; r++) {
18912 for (var i =0; i < Date.dayNames.length; i++) {
18913 var d = Date.dayNames[i];
18914 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18917 row.cn[0].cls+=' fc-first';
18918 row.cn[0].cn[0].style = 'min-height:90px';
18919 row.cn[6].cls+=' fc-last';
18923 ret[0].cls += ' fc-first';
18924 ret[4].cls += ' fc-prev-last';
18925 ret[5].cls += ' fc-last';
18932 cls: 'fc-border-separate',
18933 style : 'width:100%',
18941 cls : 'fc-first fc-last',
18959 cls : 'fc-content',
18960 style : "position: relative;",
18963 cls : 'fc-view fc-view-month fc-grid',
18964 style : 'position: relative',
18965 unselectable : 'on',
18968 cls : 'fc-event-container',
18969 style : 'position:absolute;z-index:8;top:0;left:0;'
18987 initEvents : function()
18990 throw "can not find store for calendar";
18996 style: "text-align:center",
19000 style: "background-color:white;width:50%;margin:250 auto",
19004 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19015 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19017 var size = this.el.select('.fc-content', true).first().getSize();
19018 this.maskEl.setSize(size.width, size.height);
19019 this.maskEl.enableDisplayMode("block");
19020 if(!this.loadMask){
19021 this.maskEl.hide();
19024 this.store = Roo.factory(this.store, Roo.data);
19025 this.store.on('load', this.onLoad, this);
19026 this.store.on('beforeload', this.onBeforeLoad, this);
19030 this.cells = this.el.select('.fc-day',true);
19031 //Roo.log(this.cells);
19032 this.textNodes = this.el.query('.fc-day-number');
19033 this.cells.addClassOnOver('fc-state-hover');
19035 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19036 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19037 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19038 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19040 this.on('monthchange', this.onMonthChange, this);
19042 this.update(new Date().clearTime());
19045 resize : function() {
19046 var sz = this.el.getSize();
19048 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19049 this.el.select('.fc-day-content div',true).setHeight(34);
19054 showPrevMonth : function(e){
19055 this.update(this.activeDate.add("mo", -1));
19057 showToday : function(e){
19058 this.update(new Date().clearTime());
19061 showNextMonth : function(e){
19062 this.update(this.activeDate.add("mo", 1));
19066 showPrevYear : function(){
19067 this.update(this.activeDate.add("y", -1));
19071 showNextYear : function(){
19072 this.update(this.activeDate.add("y", 1));
19077 update : function(date)
19079 var vd = this.activeDate;
19080 this.activeDate = date;
19081 // if(vd && this.el){
19082 // var t = date.getTime();
19083 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19084 // Roo.log('using add remove');
19086 // this.fireEvent('monthchange', this, date);
19088 // this.cells.removeClass("fc-state-highlight");
19089 // this.cells.each(function(c){
19090 // if(c.dateValue == t){
19091 // c.addClass("fc-state-highlight");
19092 // setTimeout(function(){
19093 // try{c.dom.firstChild.focus();}catch(e){}
19103 var days = date.getDaysInMonth();
19105 var firstOfMonth = date.getFirstDateOfMonth();
19106 var startingPos = firstOfMonth.getDay()-this.startDay;
19108 if(startingPos < this.startDay){
19112 var pm = date.add(Date.MONTH, -1);
19113 var prevStart = pm.getDaysInMonth()-startingPos;
19115 this.cells = this.el.select('.fc-day',true);
19116 this.textNodes = this.el.query('.fc-day-number');
19117 this.cells.addClassOnOver('fc-state-hover');
19119 var cells = this.cells.elements;
19120 var textEls = this.textNodes;
19122 Roo.each(cells, function(cell){
19123 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19126 days += startingPos;
19128 // convert everything to numbers so it's fast
19129 var day = 86400000;
19130 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19133 //Roo.log(prevStart);
19135 var today = new Date().clearTime().getTime();
19136 var sel = date.clearTime().getTime();
19137 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19138 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19139 var ddMatch = this.disabledDatesRE;
19140 var ddText = this.disabledDatesText;
19141 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19142 var ddaysText = this.disabledDaysText;
19143 var format = this.format;
19145 var setCellClass = function(cal, cell){
19149 //Roo.log('set Cell Class');
19151 var t = d.getTime();
19155 cell.dateValue = t;
19157 cell.className += " fc-today";
19158 cell.className += " fc-state-highlight";
19159 cell.title = cal.todayText;
19162 // disable highlight in other month..
19163 //cell.className += " fc-state-highlight";
19168 cell.className = " fc-state-disabled";
19169 cell.title = cal.minText;
19173 cell.className = " fc-state-disabled";
19174 cell.title = cal.maxText;
19178 if(ddays.indexOf(d.getDay()) != -1){
19179 cell.title = ddaysText;
19180 cell.className = " fc-state-disabled";
19183 if(ddMatch && format){
19184 var fvalue = d.dateFormat(format);
19185 if(ddMatch.test(fvalue)){
19186 cell.title = ddText.replace("%0", fvalue);
19187 cell.className = " fc-state-disabled";
19191 if (!cell.initialClassName) {
19192 cell.initialClassName = cell.dom.className;
19195 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19200 for(; i < startingPos; i++) {
19201 textEls[i].innerHTML = (++prevStart);
19202 d.setDate(d.getDate()+1);
19204 cells[i].className = "fc-past fc-other-month";
19205 setCellClass(this, cells[i]);
19210 for(; i < days; i++){
19211 intDay = i - startingPos + 1;
19212 textEls[i].innerHTML = (intDay);
19213 d.setDate(d.getDate()+1);
19215 cells[i].className = ''; // "x-date-active";
19216 setCellClass(this, cells[i]);
19220 for(; i < 42; i++) {
19221 textEls[i].innerHTML = (++extraDays);
19222 d.setDate(d.getDate()+1);
19224 cells[i].className = "fc-future fc-other-month";
19225 setCellClass(this, cells[i]);
19228 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19230 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19232 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19233 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19235 if(totalRows != 6){
19236 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19237 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19240 this.fireEvent('monthchange', this, date);
19244 if(!this.internalRender){
19245 var main = this.el.dom.firstChild;
19246 var w = main.offsetWidth;
19247 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19248 Roo.fly(main).setWidth(w);
19249 this.internalRender = true;
19250 // opera does not respect the auto grow header center column
19251 // then, after it gets a width opera refuses to recalculate
19252 // without a second pass
19253 if(Roo.isOpera && !this.secondPass){
19254 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19255 this.secondPass = true;
19256 this.update.defer(10, this, [date]);
19263 findCell : function(dt) {
19264 dt = dt.clearTime().getTime();
19266 this.cells.each(function(c){
19267 //Roo.log("check " +c.dateValue + '?=' + dt);
19268 if(c.dateValue == dt){
19278 findCells : function(ev) {
19279 var s = ev.start.clone().clearTime().getTime();
19281 var e= ev.end.clone().clearTime().getTime();
19284 this.cells.each(function(c){
19285 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19287 if(c.dateValue > e){
19290 if(c.dateValue < s){
19299 // findBestRow: function(cells)
19303 // for (var i =0 ; i < cells.length;i++) {
19304 // ret = Math.max(cells[i].rows || 0,ret);
19311 addItem : function(ev)
19313 // look for vertical location slot in
19314 var cells = this.findCells(ev);
19316 // ev.row = this.findBestRow(cells);
19318 // work out the location.
19322 for(var i =0; i < cells.length; i++) {
19324 cells[i].row = cells[0].row;
19327 cells[i].row = cells[i].row + 1;
19337 if (crow.start.getY() == cells[i].getY()) {
19339 crow.end = cells[i];
19356 cells[0].events.push(ev);
19358 this.calevents.push(ev);
19361 clearEvents: function() {
19363 if(!this.calevents){
19367 Roo.each(this.cells.elements, function(c){
19373 Roo.each(this.calevents, function(e) {
19374 Roo.each(e.els, function(el) {
19375 el.un('mouseenter' ,this.onEventEnter, this);
19376 el.un('mouseleave' ,this.onEventLeave, this);
19381 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19387 renderEvents: function()
19391 this.cells.each(function(c) {
19400 if(c.row != c.events.length){
19401 r = 4 - (4 - (c.row - c.events.length));
19404 c.events = ev.slice(0, r);
19405 c.more = ev.slice(r);
19407 if(c.more.length && c.more.length == 1){
19408 c.events.push(c.more.pop());
19411 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19415 this.cells.each(function(c) {
19417 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19420 for (var e = 0; e < c.events.length; e++){
19421 var ev = c.events[e];
19422 var rows = ev.rows;
19424 for(var i = 0; i < rows.length; i++) {
19426 // how many rows should it span..
19429 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19430 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19432 unselectable : "on",
19435 cls: 'fc-event-inner',
19439 // cls: 'fc-event-time',
19440 // html : cells.length > 1 ? '' : ev.time
19444 cls: 'fc-event-title',
19445 html : String.format('{0}', ev.title)
19452 cls: 'ui-resizable-handle ui-resizable-e',
19453 html : '  '
19460 cfg.cls += ' fc-event-start';
19462 if ((i+1) == rows.length) {
19463 cfg.cls += ' fc-event-end';
19466 var ctr = _this.el.select('.fc-event-container',true).first();
19467 var cg = ctr.createChild(cfg);
19469 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19470 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19472 var r = (c.more.length) ? 1 : 0;
19473 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19474 cg.setWidth(ebox.right - sbox.x -2);
19476 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19477 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19478 cg.on('click', _this.onEventClick, _this, ev);
19489 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19490 style : 'position: absolute',
19491 unselectable : "on",
19494 cls: 'fc-event-inner',
19498 cls: 'fc-event-title',
19506 cls: 'ui-resizable-handle ui-resizable-e',
19507 html : '  '
19513 var ctr = _this.el.select('.fc-event-container',true).first();
19514 var cg = ctr.createChild(cfg);
19516 var sbox = c.select('.fc-day-content',true).first().getBox();
19517 var ebox = c.select('.fc-day-content',true).first().getBox();
19519 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19520 cg.setWidth(ebox.right - sbox.x -2);
19522 cg.on('click', _this.onMoreEventClick, _this, c.more);
19532 onEventEnter: function (e, el,event,d) {
19533 this.fireEvent('evententer', this, el, event);
19536 onEventLeave: function (e, el,event,d) {
19537 this.fireEvent('eventleave', this, el, event);
19540 onEventClick: function (e, el,event,d) {
19541 this.fireEvent('eventclick', this, el, event);
19544 onMonthChange: function () {
19548 onMoreEventClick: function(e, el, more)
19552 this.calpopover.placement = 'right';
19553 this.calpopover.setTitle('More');
19555 this.calpopover.setContent('');
19557 var ctr = this.calpopover.el.select('.popover-content', true).first();
19559 Roo.each(more, function(m){
19561 cls : 'fc-event-hori fc-event-draggable',
19564 var cg = ctr.createChild(cfg);
19566 cg.on('click', _this.onEventClick, _this, m);
19569 this.calpopover.show(el);
19574 onLoad: function ()
19576 this.calevents = [];
19579 if(this.store.getCount() > 0){
19580 this.store.data.each(function(d){
19583 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19584 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19585 time : d.data.start_time,
19586 title : d.data.title,
19587 description : d.data.description,
19588 venue : d.data.venue
19593 this.renderEvents();
19595 if(this.calevents.length && this.loadMask){
19596 this.maskEl.hide();
19600 onBeforeLoad: function()
19602 this.clearEvents();
19604 this.maskEl.show();
19618 * @class Roo.bootstrap.Popover
19619 * @extends Roo.bootstrap.Component
19620 * Bootstrap Popover class
19621 * @cfg {String} html contents of the popover (or false to use children..)
19622 * @cfg {String} title of popover (or false to hide)
19623 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19624 * @cfg {String} trigger click || hover (or false to trigger manually)
19625 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19626 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19627 * - if false and it has a 'parent' then it will be automatically added to that element
19628 * - if string - Roo.get will be called
19629 * @cfg {Number} delay - delay before showing
19632 * Create a new Popover
19633 * @param {Object} config The config object
19636 Roo.bootstrap.Popover = function(config){
19637 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19643 * After the popover show
19645 * @param {Roo.bootstrap.Popover} this
19650 * After the popover hide
19652 * @param {Roo.bootstrap.Popover} this
19658 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19663 placement : 'right',
19664 trigger : 'hover', // hover
19670 can_build_overlaid : false,
19672 maskEl : false, // the mask element
19675 alignEl : false, // when show is called with an element - this get's stored.
19677 getChildContainer : function()
19679 return this.contentEl;
19682 getPopoverHeader : function()
19684 this.title = true; // flag not to hide it..
19685 this.headerEl.addClass('p-0');
19686 return this.headerEl
19690 getAutoCreate : function(){
19693 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19694 style: 'display:block',
19700 cls : 'popover-inner ',
19704 cls: 'popover-title popover-header',
19705 html : this.title === false ? '' : this.title
19708 cls : 'popover-content popover-body ' + (this.cls || ''),
19709 html : this.html || ''
19720 * @param {string} the title
19722 setTitle: function(str)
19726 this.headerEl.dom.innerHTML = str;
19731 * @param {string} the body content
19733 setContent: function(str)
19736 if (this.contentEl) {
19737 this.contentEl.dom.innerHTML = str;
19741 // as it get's added to the bottom of the page.
19742 onRender : function(ct, position)
19744 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19749 var cfg = Roo.apply({}, this.getAutoCreate());
19753 cfg.cls += ' ' + this.cls;
19756 cfg.style = this.style;
19758 //Roo.log("adding to ");
19759 this.el = Roo.get(document.body).createChild(cfg, position);
19760 // Roo.log(this.el);
19763 this.contentEl = this.el.select('.popover-content',true).first();
19764 this.headerEl = this.el.select('.popover-title',true).first();
19767 if(typeof(this.items) != 'undefined'){
19768 var items = this.items;
19771 for(var i =0;i < items.length;i++) {
19772 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19776 this.items = nitems;
19778 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19779 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19786 resizeMask : function()
19788 this.maskEl.setSize(
19789 Roo.lib.Dom.getViewWidth(true),
19790 Roo.lib.Dom.getViewHeight(true)
19794 initEvents : function()
19798 Roo.bootstrap.Popover.register(this);
19801 this.arrowEl = this.el.select('.arrow',true).first();
19802 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19803 this.el.enableDisplayMode('block');
19807 if (this.over === false && !this.parent()) {
19810 if (this.triggers === false) {
19815 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19816 var triggers = this.trigger ? this.trigger.split(' ') : [];
19817 Roo.each(triggers, function(trigger) {
19819 if (trigger == 'click') {
19820 on_el.on('click', this.toggle, this);
19821 } else if (trigger != 'manual') {
19822 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19823 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19825 on_el.on(eventIn ,this.enter, this);
19826 on_el.on(eventOut, this.leave, this);
19836 toggle : function () {
19837 this.hoverState == 'in' ? this.leave() : this.enter();
19840 enter : function () {
19842 clearTimeout(this.timeout);
19844 this.hoverState = 'in';
19846 if (!this.delay || !this.delay.show) {
19851 this.timeout = setTimeout(function () {
19852 if (_t.hoverState == 'in') {
19855 }, this.delay.show)
19858 leave : function() {
19859 clearTimeout(this.timeout);
19861 this.hoverState = 'out';
19863 if (!this.delay || !this.delay.hide) {
19868 this.timeout = setTimeout(function () {
19869 if (_t.hoverState == 'out') {
19872 }, this.delay.hide)
19876 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19877 * @param {string} (left|right|top|bottom) position
19879 show : function (on_el, placement)
19881 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19882 on_el = on_el || false; // default to false
19885 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19886 on_el = this.parent().el;
19887 } else if (this.over) {
19888 Roo.get(this.over);
19893 this.alignEl = Roo.get( on_el );
19896 this.render(document.body);
19902 if (this.title === false) {
19903 this.headerEl.hide();
19908 this.el.dom.style.display = 'block';
19911 if (this.alignEl) {
19912 this.updatePosition(this.placement, true);
19915 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19916 var es = this.el.getSize();
19917 var x = Roo.lib.Dom.getViewWidth()/2;
19918 var y = Roo.lib.Dom.getViewHeight()/2;
19919 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19924 //var arrow = this.el.select('.arrow',true).first();
19925 //arrow.set(align[2],
19927 this.el.addClass('in');
19931 this.hoverState = 'in';
19934 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19935 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19936 this.maskEl.dom.style.display = 'block';
19937 this.maskEl.addClass('show');
19939 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19941 this.fireEvent('show', this);
19945 * fire this manually after loading a grid in the table for example
19946 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19947 * @param {Boolean} try and move it if we cant get right position.
19949 updatePosition : function(placement, try_move)
19951 // allow for calling with no parameters
19952 placement = placement ? placement : this.placement;
19953 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19955 this.el.removeClass([
19956 'fade','top','bottom', 'left', 'right','in',
19957 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19959 this.el.addClass(placement + ' bs-popover-' + placement);
19961 if (!this.alignEl ) {
19965 switch (placement) {
19967 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19968 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19969 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19970 //normal display... or moved up/down.
19971 this.el.setXY(offset);
19972 var xy = this.alignEl.getAnchorXY('tr', false);
19974 this.arrowEl.setXY(xy);
19977 // continue through...
19978 return this.updatePosition('left', false);
19982 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19983 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19984 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19985 //normal display... or moved up/down.
19986 this.el.setXY(offset);
19987 var xy = this.alignEl.getAnchorXY('tl', false);
19988 xy[0]-=10;xy[1]+=5; // << fix me
19989 this.arrowEl.setXY(xy);
19993 return this.updatePosition('right', false);
19996 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
19997 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
19998 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19999 //normal display... or moved up/down.
20000 this.el.setXY(offset);
20001 var xy = this.alignEl.getAnchorXY('t', false);
20002 xy[1]-=10; // << fix me
20003 this.arrowEl.setXY(xy);
20007 return this.updatePosition('bottom', false);
20010 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20011 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20012 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20013 //normal display... or moved up/down.
20014 this.el.setXY(offset);
20015 var xy = this.alignEl.getAnchorXY('b', false);
20016 xy[1]+=2; // << fix me
20017 this.arrowEl.setXY(xy);
20021 return this.updatePosition('top', false);
20032 this.el.setXY([0,0]);
20033 this.el.removeClass('in');
20035 this.hoverState = null;
20036 this.maskEl.hide(); // always..
20037 this.fireEvent('hide', this);
20043 Roo.apply(Roo.bootstrap.Popover, {
20046 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20047 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20048 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20049 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20054 clickHander : false,
20057 onMouseDown : function(e)
20059 if (!e.getTarget(".roo-popover")) {
20067 register : function(popup)
20069 if (!Roo.bootstrap.Popover.clickHandler) {
20070 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20072 // hide other popups.
20074 this.popups.push(popup);
20076 hideAll : function()
20078 this.popups.forEach(function(p) {
20086 * Card header - holder for the card header elements.
20091 * @class Roo.bootstrap.PopoverNav
20092 * @extends Roo.bootstrap.NavGroup
20093 * Bootstrap Popover header navigation class
20095 * Create a new Popover Header Navigation
20096 * @param {Object} config The config object
20099 Roo.bootstrap.PopoverNav = function(config){
20100 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20103 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20106 container_method : 'getPopoverHeader'
20124 * @class Roo.bootstrap.Progress
20125 * @extends Roo.bootstrap.Component
20126 * Bootstrap Progress class
20127 * @cfg {Boolean} striped striped of the progress bar
20128 * @cfg {Boolean} active animated of the progress bar
20132 * Create a new Progress
20133 * @param {Object} config The config object
20136 Roo.bootstrap.Progress = function(config){
20137 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20140 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20145 getAutoCreate : function(){
20153 cfg.cls += ' progress-striped';
20157 cfg.cls += ' active';
20176 * @class Roo.bootstrap.ProgressBar
20177 * @extends Roo.bootstrap.Component
20178 * Bootstrap ProgressBar class
20179 * @cfg {Number} aria_valuenow aria-value now
20180 * @cfg {Number} aria_valuemin aria-value min
20181 * @cfg {Number} aria_valuemax aria-value max
20182 * @cfg {String} label label for the progress bar
20183 * @cfg {String} panel (success | info | warning | danger )
20184 * @cfg {String} role role of the progress bar
20185 * @cfg {String} sr_only text
20189 * Create a new ProgressBar
20190 * @param {Object} config The config object
20193 Roo.bootstrap.ProgressBar = function(config){
20194 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20197 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20201 aria_valuemax : 100,
20207 getAutoCreate : function()
20212 cls: 'progress-bar',
20213 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20225 cfg.role = this.role;
20228 if(this.aria_valuenow){
20229 cfg['aria-valuenow'] = this.aria_valuenow;
20232 if(this.aria_valuemin){
20233 cfg['aria-valuemin'] = this.aria_valuemin;
20236 if(this.aria_valuemax){
20237 cfg['aria-valuemax'] = this.aria_valuemax;
20240 if(this.label && !this.sr_only){
20241 cfg.html = this.label;
20245 cfg.cls += ' progress-bar-' + this.panel;
20251 update : function(aria_valuenow)
20253 this.aria_valuenow = aria_valuenow;
20255 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20270 * @class Roo.bootstrap.TabGroup
20271 * @extends Roo.bootstrap.Column
20272 * Bootstrap Column class
20273 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20274 * @cfg {Boolean} carousel true to make the group behave like a carousel
20275 * @cfg {Boolean} bullets show bullets for the panels
20276 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20277 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20278 * @cfg {Boolean} showarrow (true|false) show arrow default true
20281 * Create a new TabGroup
20282 * @param {Object} config The config object
20285 Roo.bootstrap.TabGroup = function(config){
20286 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20288 this.navId = Roo.id();
20291 Roo.bootstrap.TabGroup.register(this);
20295 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20298 transition : false,
20303 slideOnTouch : false,
20306 getAutoCreate : function()
20308 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20310 cfg.cls += ' tab-content';
20312 if (this.carousel) {
20313 cfg.cls += ' carousel slide';
20316 cls : 'carousel-inner',
20320 if(this.bullets && !Roo.isTouch){
20323 cls : 'carousel-bullets',
20327 if(this.bullets_cls){
20328 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20335 cfg.cn[0].cn.push(bullets);
20338 if(this.showarrow){
20339 cfg.cn[0].cn.push({
20341 class : 'carousel-arrow',
20345 class : 'carousel-prev',
20349 class : 'fa fa-chevron-left'
20355 class : 'carousel-next',
20359 class : 'fa fa-chevron-right'
20372 initEvents: function()
20374 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20375 // this.el.on("touchstart", this.onTouchStart, this);
20378 if(this.autoslide){
20381 this.slideFn = window.setInterval(function() {
20382 _this.showPanelNext();
20386 if(this.showarrow){
20387 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20388 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20394 // onTouchStart : function(e, el, o)
20396 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20400 // this.showPanelNext();
20404 getChildContainer : function()
20406 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20410 * register a Navigation item
20411 * @param {Roo.bootstrap.NavItem} the navitem to add
20413 register : function(item)
20415 this.tabs.push( item);
20416 item.navId = this.navId; // not really needed..
20421 getActivePanel : function()
20424 Roo.each(this.tabs, function(t) {
20434 getPanelByName : function(n)
20437 Roo.each(this.tabs, function(t) {
20438 if (t.tabId == n) {
20446 indexOfPanel : function(p)
20449 Roo.each(this.tabs, function(t,i) {
20450 if (t.tabId == p.tabId) {
20459 * show a specific panel
20460 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20461 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20463 showPanel : function (pan)
20465 if(this.transition || typeof(pan) == 'undefined'){
20466 Roo.log("waiting for the transitionend");
20470 if (typeof(pan) == 'number') {
20471 pan = this.tabs[pan];
20474 if (typeof(pan) == 'string') {
20475 pan = this.getPanelByName(pan);
20478 var cur = this.getActivePanel();
20481 Roo.log('pan or acitve pan is undefined');
20485 if (pan.tabId == this.getActivePanel().tabId) {
20489 if (false === cur.fireEvent('beforedeactivate')) {
20493 if(this.bullets > 0 && !Roo.isTouch){
20494 this.setActiveBullet(this.indexOfPanel(pan));
20497 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20499 //class="carousel-item carousel-item-next carousel-item-left"
20501 this.transition = true;
20502 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20503 var lr = dir == 'next' ? 'left' : 'right';
20504 pan.el.addClass(dir); // or prev
20505 pan.el.addClass('carousel-item-' + dir); // or prev
20506 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20507 cur.el.addClass(lr); // or right
20508 pan.el.addClass(lr);
20509 cur.el.addClass('carousel-item-' +lr); // or right
20510 pan.el.addClass('carousel-item-' +lr);
20514 cur.el.on('transitionend', function() {
20515 Roo.log("trans end?");
20517 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20518 pan.setActive(true);
20520 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20521 cur.setActive(false);
20523 _this.transition = false;
20525 }, this, { single: true } );
20530 cur.setActive(false);
20531 pan.setActive(true);
20536 showPanelNext : function()
20538 var i = this.indexOfPanel(this.getActivePanel());
20540 if (i >= this.tabs.length - 1 && !this.autoslide) {
20544 if (i >= this.tabs.length - 1 && this.autoslide) {
20548 this.showPanel(this.tabs[i+1]);
20551 showPanelPrev : function()
20553 var i = this.indexOfPanel(this.getActivePanel());
20555 if (i < 1 && !this.autoslide) {
20559 if (i < 1 && this.autoslide) {
20560 i = this.tabs.length;
20563 this.showPanel(this.tabs[i-1]);
20567 addBullet: function()
20569 if(!this.bullets || Roo.isTouch){
20572 var ctr = this.el.select('.carousel-bullets',true).first();
20573 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20574 var bullet = ctr.createChild({
20575 cls : 'bullet bullet-' + i
20576 },ctr.dom.lastChild);
20581 bullet.on('click', (function(e, el, o, ii, t){
20583 e.preventDefault();
20585 this.showPanel(ii);
20587 if(this.autoslide && this.slideFn){
20588 clearInterval(this.slideFn);
20589 this.slideFn = window.setInterval(function() {
20590 _this.showPanelNext();
20594 }).createDelegate(this, [i, bullet], true));
20599 setActiveBullet : function(i)
20605 Roo.each(this.el.select('.bullet', true).elements, function(el){
20606 el.removeClass('selected');
20609 var bullet = this.el.select('.bullet-' + i, true).first();
20615 bullet.addClass('selected');
20626 Roo.apply(Roo.bootstrap.TabGroup, {
20630 * register a Navigation Group
20631 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20633 register : function(navgrp)
20635 this.groups[navgrp.navId] = navgrp;
20639 * fetch a Navigation Group based on the navigation ID
20640 * if one does not exist , it will get created.
20641 * @param {string} the navgroup to add
20642 * @returns {Roo.bootstrap.NavGroup} the navgroup
20644 get: function(navId) {
20645 if (typeof(this.groups[navId]) == 'undefined') {
20646 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20648 return this.groups[navId] ;
20663 * @class Roo.bootstrap.TabPanel
20664 * @extends Roo.bootstrap.Component
20665 * Bootstrap TabPanel class
20666 * @cfg {Boolean} active panel active
20667 * @cfg {String} html panel content
20668 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20669 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20670 * @cfg {String} href click to link..
20671 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20675 * Create a new TabPanel
20676 * @param {Object} config The config object
20679 Roo.bootstrap.TabPanel = function(config){
20680 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20684 * Fires when the active status changes
20685 * @param {Roo.bootstrap.TabPanel} this
20686 * @param {Boolean} state the new state
20691 * @event beforedeactivate
20692 * Fires before a tab is de-activated - can be used to do validation on a form.
20693 * @param {Roo.bootstrap.TabPanel} this
20694 * @return {Boolean} false if there is an error
20697 'beforedeactivate': true
20700 this.tabId = this.tabId || Roo.id();
20704 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20711 touchSlide : false,
20712 getAutoCreate : function(){
20717 // item is needed for carousel - not sure if it has any effect otherwise
20718 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20719 html: this.html || ''
20723 cfg.cls += ' active';
20727 cfg.tabId = this.tabId;
20735 initEvents: function()
20737 var p = this.parent();
20739 this.navId = this.navId || p.navId;
20741 if (typeof(this.navId) != 'undefined') {
20742 // not really needed.. but just in case.. parent should be a NavGroup.
20743 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20747 var i = tg.tabs.length - 1;
20749 if(this.active && tg.bullets > 0 && i < tg.bullets){
20750 tg.setActiveBullet(i);
20754 this.el.on('click', this.onClick, this);
20756 if(Roo.isTouch && this.touchSlide){
20757 this.el.on("touchstart", this.onTouchStart, this);
20758 this.el.on("touchmove", this.onTouchMove, this);
20759 this.el.on("touchend", this.onTouchEnd, this);
20764 onRender : function(ct, position)
20766 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20769 setActive : function(state)
20771 Roo.log("panel - set active " + this.tabId + "=" + state);
20773 this.active = state;
20775 this.el.removeClass('active');
20777 } else if (!this.el.hasClass('active')) {
20778 this.el.addClass('active');
20781 this.fireEvent('changed', this, state);
20784 onClick : function(e)
20786 e.preventDefault();
20788 if(!this.href.length){
20792 window.location.href = this.href;
20801 onTouchStart : function(e)
20803 this.swiping = false;
20805 this.startX = e.browserEvent.touches[0].clientX;
20806 this.startY = e.browserEvent.touches[0].clientY;
20809 onTouchMove : function(e)
20811 this.swiping = true;
20813 this.endX = e.browserEvent.touches[0].clientX;
20814 this.endY = e.browserEvent.touches[0].clientY;
20817 onTouchEnd : function(e)
20824 var tabGroup = this.parent();
20826 if(this.endX > this.startX){ // swiping right
20827 tabGroup.showPanelPrev();
20831 if(this.startX > this.endX){ // swiping left
20832 tabGroup.showPanelNext();
20851 * @class Roo.bootstrap.DateField
20852 * @extends Roo.bootstrap.Input
20853 * Bootstrap DateField class
20854 * @cfg {Number} weekStart default 0
20855 * @cfg {String} viewMode default empty, (months|years)
20856 * @cfg {String} minViewMode default empty, (months|years)
20857 * @cfg {Number} startDate default -Infinity
20858 * @cfg {Number} endDate default Infinity
20859 * @cfg {Boolean} todayHighlight default false
20860 * @cfg {Boolean} todayBtn default false
20861 * @cfg {Boolean} calendarWeeks default false
20862 * @cfg {Object} daysOfWeekDisabled default empty
20863 * @cfg {Boolean} singleMode default false (true | false)
20865 * @cfg {Boolean} keyboardNavigation default true
20866 * @cfg {String} language default en
20869 * Create a new DateField
20870 * @param {Object} config The config object
20873 Roo.bootstrap.DateField = function(config){
20874 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20878 * Fires when this field show.
20879 * @param {Roo.bootstrap.DateField} this
20880 * @param {Mixed} date The date value
20885 * Fires when this field hide.
20886 * @param {Roo.bootstrap.DateField} this
20887 * @param {Mixed} date The date value
20892 * Fires when select a date.
20893 * @param {Roo.bootstrap.DateField} this
20894 * @param {Mixed} date The date value
20898 * @event beforeselect
20899 * Fires when before select a date.
20900 * @param {Roo.bootstrap.DateField} this
20901 * @param {Mixed} date The date value
20903 beforeselect : true
20907 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20910 * @cfg {String} format
20911 * The default date format string which can be overriden for localization support. The format must be
20912 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20916 * @cfg {String} altFormats
20917 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20918 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20920 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20928 todayHighlight : false,
20934 keyboardNavigation: true,
20936 calendarWeeks: false,
20938 startDate: -Infinity,
20942 daysOfWeekDisabled: [],
20946 singleMode : false,
20948 UTCDate: function()
20950 return new Date(Date.UTC.apply(Date, arguments));
20953 UTCToday: function()
20955 var today = new Date();
20956 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20959 getDate: function() {
20960 var d = this.getUTCDate();
20961 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20964 getUTCDate: function() {
20968 setDate: function(d) {
20969 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20972 setUTCDate: function(d) {
20974 this.setValue(this.formatDate(this.date));
20977 onRender: function(ct, position)
20980 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20982 this.language = this.language || 'en';
20983 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20984 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20986 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20987 this.format = this.format || 'm/d/y';
20988 this.isInline = false;
20989 this.isInput = true;
20990 this.component = this.el.select('.add-on', true).first() || false;
20991 this.component = (this.component && this.component.length === 0) ? false : this.component;
20992 this.hasInput = this.component && this.inputEl().length;
20994 if (typeof(this.minViewMode === 'string')) {
20995 switch (this.minViewMode) {
20997 this.minViewMode = 1;
21000 this.minViewMode = 2;
21003 this.minViewMode = 0;
21008 if (typeof(this.viewMode === 'string')) {
21009 switch (this.viewMode) {
21022 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21024 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21026 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21028 this.picker().on('mousedown', this.onMousedown, this);
21029 this.picker().on('click', this.onClick, this);
21031 this.picker().addClass('datepicker-dropdown');
21033 this.startViewMode = this.viewMode;
21035 if(this.singleMode){
21036 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21037 v.setVisibilityMode(Roo.Element.DISPLAY);
21041 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21042 v.setStyle('width', '189px');
21046 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21047 if(!this.calendarWeeks){
21052 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21053 v.attr('colspan', function(i, val){
21054 return parseInt(val) + 1;
21059 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21061 this.setStartDate(this.startDate);
21062 this.setEndDate(this.endDate);
21064 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21071 if(this.isInline) {
21076 picker : function()
21078 return this.pickerEl;
21079 // return this.el.select('.datepicker', true).first();
21082 fillDow: function()
21084 var dowCnt = this.weekStart;
21093 if(this.calendarWeeks){
21101 while (dowCnt < this.weekStart + 7) {
21105 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21109 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21112 fillMonths: function()
21115 var months = this.picker().select('>.datepicker-months td', true).first();
21117 months.dom.innerHTML = '';
21123 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21126 months.createChild(month);
21133 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;
21135 if (this.date < this.startDate) {
21136 this.viewDate = new Date(this.startDate);
21137 } else if (this.date > this.endDate) {
21138 this.viewDate = new Date(this.endDate);
21140 this.viewDate = new Date(this.date);
21148 var d = new Date(this.viewDate),
21149 year = d.getUTCFullYear(),
21150 month = d.getUTCMonth(),
21151 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21152 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21153 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21154 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21155 currentDate = this.date && this.date.valueOf(),
21156 today = this.UTCToday();
21158 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21160 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21162 // this.picker.select('>tfoot th.today').
21163 // .text(dates[this.language].today)
21164 // .toggle(this.todayBtn !== false);
21166 this.updateNavArrows();
21169 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21171 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21173 prevMonth.setUTCDate(day);
21175 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21177 var nextMonth = new Date(prevMonth);
21179 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21181 nextMonth = nextMonth.valueOf();
21183 var fillMonths = false;
21185 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21187 while(prevMonth.valueOf() <= nextMonth) {
21190 if (prevMonth.getUTCDay() === this.weekStart) {
21192 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21200 if(this.calendarWeeks){
21201 // ISO 8601: First week contains first thursday.
21202 // ISO also states week starts on Monday, but we can be more abstract here.
21204 // Start of current week: based on weekstart/current date
21205 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21206 // Thursday of this week
21207 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21208 // First Thursday of year, year from thursday
21209 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21210 // Calendar week: ms between thursdays, div ms per day, div 7 days
21211 calWeek = (th - yth) / 864e5 / 7 + 1;
21213 fillMonths.cn.push({
21221 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21223 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21226 if (this.todayHighlight &&
21227 prevMonth.getUTCFullYear() == today.getFullYear() &&
21228 prevMonth.getUTCMonth() == today.getMonth() &&
21229 prevMonth.getUTCDate() == today.getDate()) {
21230 clsName += ' today';
21233 if (currentDate && prevMonth.valueOf() === currentDate) {
21234 clsName += ' active';
21237 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21238 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21239 clsName += ' disabled';
21242 fillMonths.cn.push({
21244 cls: 'day ' + clsName,
21245 html: prevMonth.getDate()
21248 prevMonth.setDate(prevMonth.getDate()+1);
21251 var currentYear = this.date && this.date.getUTCFullYear();
21252 var currentMonth = this.date && this.date.getUTCMonth();
21254 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21256 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21257 v.removeClass('active');
21259 if(currentYear === year && k === currentMonth){
21260 v.addClass('active');
21263 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21264 v.addClass('disabled');
21270 year = parseInt(year/10, 10) * 10;
21272 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21274 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21277 for (var i = -1; i < 11; i++) {
21278 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21280 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21288 showMode: function(dir)
21291 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21294 Roo.each(this.picker().select('>div',true).elements, function(v){
21295 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21298 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21303 if(this.isInline) {
21307 this.picker().removeClass(['bottom', 'top']);
21309 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21311 * place to the top of element!
21315 this.picker().addClass('top');
21316 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21321 this.picker().addClass('bottom');
21323 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21326 parseDate : function(value)
21328 if(!value || value instanceof Date){
21331 var v = Date.parseDate(value, this.format);
21332 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21333 v = Date.parseDate(value, 'Y-m-d');
21335 if(!v && this.altFormats){
21336 if(!this.altFormatsArray){
21337 this.altFormatsArray = this.altFormats.split("|");
21339 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21340 v = Date.parseDate(value, this.altFormatsArray[i]);
21346 formatDate : function(date, fmt)
21348 return (!date || !(date instanceof Date)) ?
21349 date : date.dateFormat(fmt || this.format);
21352 onFocus : function()
21354 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21358 onBlur : function()
21360 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21362 var d = this.inputEl().getValue();
21369 showPopup : function()
21371 this.picker().show();
21375 this.fireEvent('showpopup', this, this.date);
21378 hidePopup : function()
21380 if(this.isInline) {
21383 this.picker().hide();
21384 this.viewMode = this.startViewMode;
21387 this.fireEvent('hidepopup', this, this.date);
21391 onMousedown: function(e)
21393 e.stopPropagation();
21394 e.preventDefault();
21399 Roo.bootstrap.DateField.superclass.keyup.call(this);
21403 setValue: function(v)
21405 if(this.fireEvent('beforeselect', this, v) !== false){
21406 var d = new Date(this.parseDate(v) ).clearTime();
21408 if(isNaN(d.getTime())){
21409 this.date = this.viewDate = '';
21410 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21414 v = this.formatDate(d);
21416 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21418 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21422 this.fireEvent('select', this, this.date);
21426 getValue: function()
21428 return this.formatDate(this.date);
21431 fireKey: function(e)
21433 if (!this.picker().isVisible()){
21434 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21440 var dateChanged = false,
21442 newDate, newViewDate;
21447 e.preventDefault();
21451 if (!this.keyboardNavigation) {
21454 dir = e.keyCode == 37 ? -1 : 1;
21457 newDate = this.moveYear(this.date, dir);
21458 newViewDate = this.moveYear(this.viewDate, dir);
21459 } else if (e.shiftKey){
21460 newDate = this.moveMonth(this.date, dir);
21461 newViewDate = this.moveMonth(this.viewDate, dir);
21463 newDate = new Date(this.date);
21464 newDate.setUTCDate(this.date.getUTCDate() + dir);
21465 newViewDate = new Date(this.viewDate);
21466 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21468 if (this.dateWithinRange(newDate)){
21469 this.date = newDate;
21470 this.viewDate = newViewDate;
21471 this.setValue(this.formatDate(this.date));
21473 e.preventDefault();
21474 dateChanged = true;
21479 if (!this.keyboardNavigation) {
21482 dir = e.keyCode == 38 ? -1 : 1;
21484 newDate = this.moveYear(this.date, dir);
21485 newViewDate = this.moveYear(this.viewDate, dir);
21486 } else if (e.shiftKey){
21487 newDate = this.moveMonth(this.date, dir);
21488 newViewDate = this.moveMonth(this.viewDate, dir);
21490 newDate = new Date(this.date);
21491 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21492 newViewDate = new Date(this.viewDate);
21493 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21495 if (this.dateWithinRange(newDate)){
21496 this.date = newDate;
21497 this.viewDate = newViewDate;
21498 this.setValue(this.formatDate(this.date));
21500 e.preventDefault();
21501 dateChanged = true;
21505 this.setValue(this.formatDate(this.date));
21507 e.preventDefault();
21510 this.setValue(this.formatDate(this.date));
21524 onClick: function(e)
21526 e.stopPropagation();
21527 e.preventDefault();
21529 var target = e.getTarget();
21531 if(target.nodeName.toLowerCase() === 'i'){
21532 target = Roo.get(target).dom.parentNode;
21535 var nodeName = target.nodeName;
21536 var className = target.className;
21537 var html = target.innerHTML;
21538 //Roo.log(nodeName);
21540 switch(nodeName.toLowerCase()) {
21542 switch(className) {
21548 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21549 switch(this.viewMode){
21551 this.viewDate = this.moveMonth(this.viewDate, dir);
21555 this.viewDate = this.moveYear(this.viewDate, dir);
21561 var date = new Date();
21562 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21564 this.setValue(this.formatDate(this.date));
21571 if (className.indexOf('disabled') < 0) {
21572 this.viewDate.setUTCDate(1);
21573 if (className.indexOf('month') > -1) {
21574 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21576 var year = parseInt(html, 10) || 0;
21577 this.viewDate.setUTCFullYear(year);
21581 if(this.singleMode){
21582 this.setValue(this.formatDate(this.viewDate));
21593 //Roo.log(className);
21594 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21595 var day = parseInt(html, 10) || 1;
21596 var year = (this.viewDate || new Date()).getUTCFullYear(),
21597 month = (this.viewDate || new Date()).getUTCMonth();
21599 if (className.indexOf('old') > -1) {
21606 } else if (className.indexOf('new') > -1) {
21614 //Roo.log([year,month,day]);
21615 this.date = this.UTCDate(year, month, day,0,0,0,0);
21616 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21618 //Roo.log(this.formatDate(this.date));
21619 this.setValue(this.formatDate(this.date));
21626 setStartDate: function(startDate)
21628 this.startDate = startDate || -Infinity;
21629 if (this.startDate !== -Infinity) {
21630 this.startDate = this.parseDate(this.startDate);
21633 this.updateNavArrows();
21636 setEndDate: function(endDate)
21638 this.endDate = endDate || Infinity;
21639 if (this.endDate !== Infinity) {
21640 this.endDate = this.parseDate(this.endDate);
21643 this.updateNavArrows();
21646 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21648 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21649 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21650 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21652 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21653 return parseInt(d, 10);
21656 this.updateNavArrows();
21659 updateNavArrows: function()
21661 if(this.singleMode){
21665 var d = new Date(this.viewDate),
21666 year = d.getUTCFullYear(),
21667 month = d.getUTCMonth();
21669 Roo.each(this.picker().select('.prev', true).elements, function(v){
21671 switch (this.viewMode) {
21674 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21680 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21687 Roo.each(this.picker().select('.next', true).elements, function(v){
21689 switch (this.viewMode) {
21692 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21698 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21706 moveMonth: function(date, dir)
21711 var new_date = new Date(date.valueOf()),
21712 day = new_date.getUTCDate(),
21713 month = new_date.getUTCMonth(),
21714 mag = Math.abs(dir),
21716 dir = dir > 0 ? 1 : -1;
21719 // If going back one month, make sure month is not current month
21720 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21722 return new_date.getUTCMonth() == month;
21724 // If going forward one month, make sure month is as expected
21725 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21727 return new_date.getUTCMonth() != new_month;
21729 new_month = month + dir;
21730 new_date.setUTCMonth(new_month);
21731 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21732 if (new_month < 0 || new_month > 11) {
21733 new_month = (new_month + 12) % 12;
21736 // For magnitudes >1, move one month at a time...
21737 for (var i=0; i<mag; i++) {
21738 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21739 new_date = this.moveMonth(new_date, dir);
21741 // ...then reset the day, keeping it in the new month
21742 new_month = new_date.getUTCMonth();
21743 new_date.setUTCDate(day);
21745 return new_month != new_date.getUTCMonth();
21748 // Common date-resetting loop -- if date is beyond end of month, make it
21751 new_date.setUTCDate(--day);
21752 new_date.setUTCMonth(new_month);
21757 moveYear: function(date, dir)
21759 return this.moveMonth(date, dir*12);
21762 dateWithinRange: function(date)
21764 return date >= this.startDate && date <= this.endDate;
21770 this.picker().remove();
21773 validateValue : function(value)
21775 if(this.getVisibilityEl().hasClass('hidden')){
21779 if(value.length < 1) {
21780 if(this.allowBlank){
21786 if(value.length < this.minLength){
21789 if(value.length > this.maxLength){
21793 var vt = Roo.form.VTypes;
21794 if(!vt[this.vtype](value, this)){
21798 if(typeof this.validator == "function"){
21799 var msg = this.validator(value);
21805 if(this.regex && !this.regex.test(value)){
21809 if(typeof(this.parseDate(value)) == 'undefined'){
21813 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21817 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21827 this.date = this.viewDate = '';
21829 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21834 Roo.apply(Roo.bootstrap.DateField, {
21845 html: '<i class="fa fa-arrow-left"/>'
21855 html: '<i class="fa fa-arrow-right"/>'
21897 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21898 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21899 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21900 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21901 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21914 navFnc: 'FullYear',
21919 navFnc: 'FullYear',
21924 Roo.apply(Roo.bootstrap.DateField, {
21928 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21932 cls: 'datepicker-days',
21936 cls: 'table-condensed',
21938 Roo.bootstrap.DateField.head,
21942 Roo.bootstrap.DateField.footer
21949 cls: 'datepicker-months',
21953 cls: 'table-condensed',
21955 Roo.bootstrap.DateField.head,
21956 Roo.bootstrap.DateField.content,
21957 Roo.bootstrap.DateField.footer
21964 cls: 'datepicker-years',
21968 cls: 'table-condensed',
21970 Roo.bootstrap.DateField.head,
21971 Roo.bootstrap.DateField.content,
21972 Roo.bootstrap.DateField.footer
21991 * @class Roo.bootstrap.TimeField
21992 * @extends Roo.bootstrap.Input
21993 * Bootstrap DateField class
21997 * Create a new TimeField
21998 * @param {Object} config The config object
22001 Roo.bootstrap.TimeField = function(config){
22002 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22006 * Fires when this field show.
22007 * @param {Roo.bootstrap.DateField} thisthis
22008 * @param {Mixed} date The date value
22013 * Fires when this field hide.
22014 * @param {Roo.bootstrap.DateField} this
22015 * @param {Mixed} date The date value
22020 * Fires when select a date.
22021 * @param {Roo.bootstrap.DateField} this
22022 * @param {Mixed} date The date value
22028 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22031 * @cfg {String} format
22032 * The default time format string which can be overriden for localization support. The format must be
22033 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22037 getAutoCreate : function()
22039 this.after = '<i class="fa far fa-clock"></i>';
22040 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22044 onRender: function(ct, position)
22047 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22049 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22051 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22053 this.pop = this.picker().select('>.datepicker-time',true).first();
22054 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22056 this.picker().on('mousedown', this.onMousedown, this);
22057 this.picker().on('click', this.onClick, this);
22059 this.picker().addClass('datepicker-dropdown');
22064 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22065 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22066 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22067 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22068 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22069 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22073 fireKey: function(e){
22074 if (!this.picker().isVisible()){
22075 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22081 e.preventDefault();
22089 this.onTogglePeriod();
22092 this.onIncrementMinutes();
22095 this.onDecrementMinutes();
22104 onClick: function(e) {
22105 e.stopPropagation();
22106 e.preventDefault();
22109 picker : function()
22111 return this.pickerEl;
22114 fillTime: function()
22116 var time = this.pop.select('tbody', true).first();
22118 time.dom.innerHTML = '';
22133 cls: 'hours-up fa fas fa-chevron-up'
22153 cls: 'minutes-up fa fas fa-chevron-up'
22174 cls: 'timepicker-hour',
22189 cls: 'timepicker-minute',
22204 cls: 'btn btn-primary period',
22226 cls: 'hours-down fa fas fa-chevron-down'
22246 cls: 'minutes-down fa fas fa-chevron-down'
22264 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22271 var hours = this.time.getHours();
22272 var minutes = this.time.getMinutes();
22285 hours = hours - 12;
22289 hours = '0' + hours;
22293 minutes = '0' + minutes;
22296 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22297 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22298 this.pop.select('button', true).first().dom.innerHTML = period;
22304 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22306 var cls = ['bottom'];
22308 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22315 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22319 //this.picker().setXY(20000,20000);
22320 this.picker().addClass(cls.join('-'));
22324 Roo.each(cls, function(c){
22329 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22330 //_this.picker().setTop(_this.inputEl().getHeight());
22334 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22336 //_this.picker().setTop(0 - _this.picker().getHeight());
22341 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22345 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22353 onFocus : function()
22355 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22359 onBlur : function()
22361 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22367 this.picker().show();
22372 this.fireEvent('show', this, this.date);
22377 this.picker().hide();
22380 this.fireEvent('hide', this, this.date);
22383 setTime : function()
22386 this.setValue(this.time.format(this.format));
22388 this.fireEvent('select', this, this.date);
22393 onMousedown: function(e){
22394 e.stopPropagation();
22395 e.preventDefault();
22398 onIncrementHours: function()
22400 Roo.log('onIncrementHours');
22401 this.time = this.time.add(Date.HOUR, 1);
22406 onDecrementHours: function()
22408 Roo.log('onDecrementHours');
22409 this.time = this.time.add(Date.HOUR, -1);
22413 onIncrementMinutes: function()
22415 Roo.log('onIncrementMinutes');
22416 this.time = this.time.add(Date.MINUTE, 1);
22420 onDecrementMinutes: function()
22422 Roo.log('onDecrementMinutes');
22423 this.time = this.time.add(Date.MINUTE, -1);
22427 onTogglePeriod: function()
22429 Roo.log('onTogglePeriod');
22430 this.time = this.time.add(Date.HOUR, 12);
22438 Roo.apply(Roo.bootstrap.TimeField, {
22442 cls: 'datepicker dropdown-menu',
22446 cls: 'datepicker-time',
22450 cls: 'table-condensed',
22479 cls: 'btn btn-info ok',
22507 * @class Roo.bootstrap.MonthField
22508 * @extends Roo.bootstrap.Input
22509 * Bootstrap MonthField class
22511 * @cfg {String} language default en
22514 * Create a new MonthField
22515 * @param {Object} config The config object
22518 Roo.bootstrap.MonthField = function(config){
22519 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22524 * Fires when this field show.
22525 * @param {Roo.bootstrap.MonthField} this
22526 * @param {Mixed} date The date value
22531 * Fires when this field hide.
22532 * @param {Roo.bootstrap.MonthField} this
22533 * @param {Mixed} date The date value
22538 * Fires when select a date.
22539 * @param {Roo.bootstrap.MonthField} this
22540 * @param {String} oldvalue The old value
22541 * @param {String} newvalue The new value
22547 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22549 onRender: function(ct, position)
22552 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22554 this.language = this.language || 'en';
22555 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22556 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22558 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22559 this.isInline = false;
22560 this.isInput = true;
22561 this.component = this.el.select('.add-on', true).first() || false;
22562 this.component = (this.component && this.component.length === 0) ? false : this.component;
22563 this.hasInput = this.component && this.inputEL().length;
22565 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22567 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22569 this.picker().on('mousedown', this.onMousedown, this);
22570 this.picker().on('click', this.onClick, this);
22572 this.picker().addClass('datepicker-dropdown');
22574 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22575 v.setStyle('width', '189px');
22582 if(this.isInline) {
22588 setValue: function(v, suppressEvent)
22590 var o = this.getValue();
22592 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22596 if(suppressEvent !== true){
22597 this.fireEvent('select', this, o, v);
22602 getValue: function()
22607 onClick: function(e)
22609 e.stopPropagation();
22610 e.preventDefault();
22612 var target = e.getTarget();
22614 if(target.nodeName.toLowerCase() === 'i'){
22615 target = Roo.get(target).dom.parentNode;
22618 var nodeName = target.nodeName;
22619 var className = target.className;
22620 var html = target.innerHTML;
22622 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22626 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22628 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22634 picker : function()
22636 return this.pickerEl;
22639 fillMonths: function()
22642 var months = this.picker().select('>.datepicker-months td', true).first();
22644 months.dom.innerHTML = '';
22650 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22653 months.createChild(month);
22662 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22663 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22666 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22667 e.removeClass('active');
22669 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22670 e.addClass('active');
22677 if(this.isInline) {
22681 this.picker().removeClass(['bottom', 'top']);
22683 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22685 * place to the top of element!
22689 this.picker().addClass('top');
22690 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22695 this.picker().addClass('bottom');
22697 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22700 onFocus : function()
22702 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22706 onBlur : function()
22708 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22710 var d = this.inputEl().getValue();
22719 this.picker().show();
22720 this.picker().select('>.datepicker-months', true).first().show();
22724 this.fireEvent('show', this, this.date);
22729 if(this.isInline) {
22732 this.picker().hide();
22733 this.fireEvent('hide', this, this.date);
22737 onMousedown: function(e)
22739 e.stopPropagation();
22740 e.preventDefault();
22745 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22749 fireKey: function(e)
22751 if (!this.picker().isVisible()){
22752 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22763 e.preventDefault();
22767 dir = e.keyCode == 37 ? -1 : 1;
22769 this.vIndex = this.vIndex + dir;
22771 if(this.vIndex < 0){
22775 if(this.vIndex > 11){
22779 if(isNaN(this.vIndex)){
22783 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22789 dir = e.keyCode == 38 ? -1 : 1;
22791 this.vIndex = this.vIndex + dir * 4;
22793 if(this.vIndex < 0){
22797 if(this.vIndex > 11){
22801 if(isNaN(this.vIndex)){
22805 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22810 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22811 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22815 e.preventDefault();
22818 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22819 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22835 this.picker().remove();
22840 Roo.apply(Roo.bootstrap.MonthField, {
22859 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22860 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22865 Roo.apply(Roo.bootstrap.MonthField, {
22869 cls: 'datepicker dropdown-menu roo-dynamic',
22873 cls: 'datepicker-months',
22877 cls: 'table-condensed',
22879 Roo.bootstrap.DateField.content
22899 * @class Roo.bootstrap.CheckBox
22900 * @extends Roo.bootstrap.Input
22901 * Bootstrap CheckBox class
22903 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22904 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22905 * @cfg {String} boxLabel The text that appears beside the checkbox
22906 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22907 * @cfg {Boolean} checked initnal the element
22908 * @cfg {Boolean} inline inline the element (default false)
22909 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22910 * @cfg {String} tooltip label tooltip
22913 * Create a new CheckBox
22914 * @param {Object} config The config object
22917 Roo.bootstrap.CheckBox = function(config){
22918 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22923 * Fires when the element is checked or unchecked.
22924 * @param {Roo.bootstrap.CheckBox} this This input
22925 * @param {Boolean} checked The new checked value
22930 * Fires when the element is click.
22931 * @param {Roo.bootstrap.CheckBox} this This input
22938 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22940 inputType: 'checkbox',
22949 // checkbox success does not make any sense really..
22954 getAutoCreate : function()
22956 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22962 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22965 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22971 type : this.inputType,
22972 value : this.inputValue,
22973 cls : 'roo-' + this.inputType, //'form-box',
22974 placeholder : this.placeholder || ''
22978 if(this.inputType != 'radio'){
22982 cls : 'roo-hidden-value',
22983 value : this.checked ? this.inputValue : this.valueOff
22988 if (this.weight) { // Validity check?
22989 cfg.cls += " " + this.inputType + "-" + this.weight;
22992 if (this.disabled) {
22993 input.disabled=true;
22997 input.checked = this.checked;
23002 input.name = this.name;
23004 if(this.inputType != 'radio'){
23005 hidden.name = this.name;
23006 input.name = '_hidden_' + this.name;
23011 input.cls += ' input-' + this.size;
23016 ['xs','sm','md','lg'].map(function(size){
23017 if (settings[size]) {
23018 cfg.cls += ' col-' + size + '-' + settings[size];
23022 var inputblock = input;
23024 if (this.before || this.after) {
23027 cls : 'input-group',
23032 inputblock.cn.push({
23034 cls : 'input-group-addon',
23039 inputblock.cn.push(input);
23041 if(this.inputType != 'radio'){
23042 inputblock.cn.push(hidden);
23046 inputblock.cn.push({
23048 cls : 'input-group-addon',
23054 var boxLabelCfg = false;
23060 //'for': id, // box label is handled by onclick - so no for...
23062 html: this.boxLabel
23065 boxLabelCfg.tooltip = this.tooltip;
23071 if (align ==='left' && this.fieldLabel.length) {
23072 // Roo.log("left and has label");
23077 cls : 'control-label',
23078 html : this.fieldLabel
23089 cfg.cn[1].cn.push(boxLabelCfg);
23092 if(this.labelWidth > 12){
23093 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23096 if(this.labelWidth < 13 && this.labelmd == 0){
23097 this.labelmd = this.labelWidth;
23100 if(this.labellg > 0){
23101 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23102 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23105 if(this.labelmd > 0){
23106 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23107 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23110 if(this.labelsm > 0){
23111 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23112 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23115 if(this.labelxs > 0){
23116 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23117 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23120 } else if ( this.fieldLabel.length) {
23121 // Roo.log(" label");
23125 tag: this.boxLabel ? 'span' : 'label',
23127 cls: 'control-label box-input-label',
23128 //cls : 'input-group-addon',
23129 html : this.fieldLabel
23136 cfg.cn.push(boxLabelCfg);
23141 // Roo.log(" no label && no align");
23142 cfg.cn = [ inputblock ] ;
23144 cfg.cn.push(boxLabelCfg);
23152 if(this.inputType != 'radio'){
23153 cfg.cn.push(hidden);
23161 * return the real input element.
23163 inputEl: function ()
23165 return this.el.select('input.roo-' + this.inputType,true).first();
23167 hiddenEl: function ()
23169 return this.el.select('input.roo-hidden-value',true).first();
23172 labelEl: function()
23174 return this.el.select('label.control-label',true).first();
23176 /* depricated... */
23180 return this.labelEl();
23183 boxLabelEl: function()
23185 return this.el.select('label.box-label',true).first();
23188 initEvents : function()
23190 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23192 this.inputEl().on('click', this.onClick, this);
23194 if (this.boxLabel) {
23195 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23198 this.startValue = this.getValue();
23201 Roo.bootstrap.CheckBox.register(this);
23205 onClick : function(e)
23207 if(this.fireEvent('click', this, e) !== false){
23208 this.setChecked(!this.checked);
23213 setChecked : function(state,suppressEvent)
23215 this.startValue = this.getValue();
23217 if(this.inputType == 'radio'){
23219 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23220 e.dom.checked = false;
23223 this.inputEl().dom.checked = true;
23225 this.inputEl().dom.value = this.inputValue;
23227 if(suppressEvent !== true){
23228 this.fireEvent('check', this, true);
23236 this.checked = state;
23238 this.inputEl().dom.checked = state;
23241 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23243 if(suppressEvent !== true){
23244 this.fireEvent('check', this, state);
23250 getValue : function()
23252 if(this.inputType == 'radio'){
23253 return this.getGroupValue();
23256 return this.hiddenEl().dom.value;
23260 getGroupValue : function()
23262 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23266 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23269 setValue : function(v,suppressEvent)
23271 if(this.inputType == 'radio'){
23272 this.setGroupValue(v, suppressEvent);
23276 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23281 setGroupValue : function(v, suppressEvent)
23283 this.startValue = this.getValue();
23285 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23286 e.dom.checked = false;
23288 if(e.dom.value == v){
23289 e.dom.checked = true;
23293 if(suppressEvent !== true){
23294 this.fireEvent('check', this, true);
23302 validate : function()
23304 if(this.getVisibilityEl().hasClass('hidden')){
23310 (this.inputType == 'radio' && this.validateRadio()) ||
23311 (this.inputType == 'checkbox' && this.validateCheckbox())
23317 this.markInvalid();
23321 validateRadio : function()
23323 if(this.getVisibilityEl().hasClass('hidden')){
23327 if(this.allowBlank){
23333 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23334 if(!e.dom.checked){
23346 validateCheckbox : function()
23349 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23350 //return (this.getValue() == this.inputValue) ? true : false;
23353 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23361 for(var i in group){
23362 if(group[i].el.isVisible(true)){
23370 for(var i in group){
23375 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23382 * Mark this field as valid
23384 markValid : function()
23388 this.fireEvent('valid', this);
23390 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23393 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23400 if(this.inputType == 'radio'){
23401 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23402 var fg = e.findParent('.form-group', false, true);
23403 if (Roo.bootstrap.version == 3) {
23404 fg.removeClass([_this.invalidClass, _this.validClass]);
23405 fg.addClass(_this.validClass);
23407 fg.removeClass(['is-valid', 'is-invalid']);
23408 fg.addClass('is-valid');
23416 var fg = this.el.findParent('.form-group', false, true);
23417 if (Roo.bootstrap.version == 3) {
23418 fg.removeClass([this.invalidClass, this.validClass]);
23419 fg.addClass(this.validClass);
23421 fg.removeClass(['is-valid', 'is-invalid']);
23422 fg.addClass('is-valid');
23427 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23433 for(var i in group){
23434 var fg = group[i].el.findParent('.form-group', false, true);
23435 if (Roo.bootstrap.version == 3) {
23436 fg.removeClass([this.invalidClass, this.validClass]);
23437 fg.addClass(this.validClass);
23439 fg.removeClass(['is-valid', 'is-invalid']);
23440 fg.addClass('is-valid');
23446 * Mark this field as invalid
23447 * @param {String} msg The validation message
23449 markInvalid : function(msg)
23451 if(this.allowBlank){
23457 this.fireEvent('invalid', this, msg);
23459 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23462 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23466 label.markInvalid();
23469 if(this.inputType == 'radio'){
23471 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23472 var fg = e.findParent('.form-group', false, true);
23473 if (Roo.bootstrap.version == 3) {
23474 fg.removeClass([_this.invalidClass, _this.validClass]);
23475 fg.addClass(_this.invalidClass);
23477 fg.removeClass(['is-invalid', 'is-valid']);
23478 fg.addClass('is-invalid');
23486 var fg = this.el.findParent('.form-group', false, true);
23487 if (Roo.bootstrap.version == 3) {
23488 fg.removeClass([_this.invalidClass, _this.validClass]);
23489 fg.addClass(_this.invalidClass);
23491 fg.removeClass(['is-invalid', 'is-valid']);
23492 fg.addClass('is-invalid');
23497 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23503 for(var i in group){
23504 var fg = group[i].el.findParent('.form-group', false, true);
23505 if (Roo.bootstrap.version == 3) {
23506 fg.removeClass([_this.invalidClass, _this.validClass]);
23507 fg.addClass(_this.invalidClass);
23509 fg.removeClass(['is-invalid', 'is-valid']);
23510 fg.addClass('is-invalid');
23516 clearInvalid : function()
23518 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23520 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23522 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23524 if (label && label.iconEl) {
23525 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23526 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23530 disable : function()
23532 if(this.inputType != 'radio'){
23533 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23540 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23541 _this.getActionEl().addClass(this.disabledClass);
23542 e.dom.disabled = true;
23546 this.disabled = true;
23547 this.fireEvent("disable", this);
23551 enable : function()
23553 if(this.inputType != 'radio'){
23554 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23561 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23562 _this.getActionEl().removeClass(this.disabledClass);
23563 e.dom.disabled = false;
23567 this.disabled = false;
23568 this.fireEvent("enable", this);
23572 setBoxLabel : function(v)
23577 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23583 Roo.apply(Roo.bootstrap.CheckBox, {
23588 * register a CheckBox Group
23589 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23591 register : function(checkbox)
23593 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23594 this.groups[checkbox.groupId] = {};
23597 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23601 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23605 * fetch a CheckBox Group based on the group ID
23606 * @param {string} the group ID
23607 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23609 get: function(groupId) {
23610 if (typeof(this.groups[groupId]) == 'undefined') {
23614 return this.groups[groupId] ;
23627 * @class Roo.bootstrap.Radio
23628 * @extends Roo.bootstrap.Component
23629 * Bootstrap Radio class
23630 * @cfg {String} boxLabel - the label associated
23631 * @cfg {String} value - the value of radio
23634 * Create a new Radio
23635 * @param {Object} config The config object
23637 Roo.bootstrap.Radio = function(config){
23638 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23642 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23648 getAutoCreate : function()
23652 cls : 'form-group radio',
23657 html : this.boxLabel
23665 initEvents : function()
23667 this.parent().register(this);
23669 this.el.on('click', this.onClick, this);
23673 onClick : function(e)
23675 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23676 this.setChecked(true);
23680 setChecked : function(state, suppressEvent)
23682 this.parent().setValue(this.value, suppressEvent);
23686 setBoxLabel : function(v)
23691 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23706 * @class Roo.bootstrap.SecurePass
23707 * @extends Roo.bootstrap.Input
23708 * Bootstrap SecurePass class
23712 * Create a new SecurePass
23713 * @param {Object} config The config object
23716 Roo.bootstrap.SecurePass = function (config) {
23717 // these go here, so the translation tool can replace them..
23719 PwdEmpty: "Please type a password, and then retype it to confirm.",
23720 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23721 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23722 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23723 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23724 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23725 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23726 TooWeak: "Your password is Too Weak."
23728 this.meterLabel = "Password strength:";
23729 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23730 this.meterClass = [
23731 "roo-password-meter-tooweak",
23732 "roo-password-meter-weak",
23733 "roo-password-meter-medium",
23734 "roo-password-meter-strong",
23735 "roo-password-meter-grey"
23740 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23743 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23745 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23747 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23748 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23749 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23750 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23751 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23752 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23753 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23763 * @cfg {String/Object} Label for the strength meter (defaults to
23764 * 'Password strength:')
23769 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23770 * ['Weak', 'Medium', 'Strong'])
23773 pwdStrengths: false,
23786 initEvents: function ()
23788 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23790 if (this.el.is('input[type=password]') && Roo.isSafari) {
23791 this.el.on('keydown', this.SafariOnKeyDown, this);
23794 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23797 onRender: function (ct, position)
23799 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23800 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23801 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23803 this.trigger.createChild({
23808 cls: 'roo-password-meter-grey col-xs-12',
23811 //width: this.meterWidth + 'px'
23815 cls: 'roo-password-meter-text'
23821 if (this.hideTrigger) {
23822 this.trigger.setDisplayed(false);
23824 this.setSize(this.width || '', this.height || '');
23827 onDestroy: function ()
23829 if (this.trigger) {
23830 this.trigger.removeAllListeners();
23831 this.trigger.remove();
23834 this.wrap.remove();
23836 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23839 checkStrength: function ()
23841 var pwd = this.inputEl().getValue();
23842 if (pwd == this._lastPwd) {
23847 if (this.ClientSideStrongPassword(pwd)) {
23849 } else if (this.ClientSideMediumPassword(pwd)) {
23851 } else if (this.ClientSideWeakPassword(pwd)) {
23857 Roo.log('strength1: ' + strength);
23859 //var pm = this.trigger.child('div/div/div').dom;
23860 var pm = this.trigger.child('div/div');
23861 pm.removeClass(this.meterClass);
23862 pm.addClass(this.meterClass[strength]);
23865 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23867 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23869 this._lastPwd = pwd;
23873 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23875 this._lastPwd = '';
23877 var pm = this.trigger.child('div/div');
23878 pm.removeClass(this.meterClass);
23879 pm.addClass('roo-password-meter-grey');
23882 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23885 this.inputEl().dom.type='password';
23888 validateValue: function (value)
23890 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23893 if (value.length == 0) {
23894 if (this.allowBlank) {
23895 this.clearInvalid();
23899 this.markInvalid(this.errors.PwdEmpty);
23900 this.errorMsg = this.errors.PwdEmpty;
23908 if (!value.match(/[\x21-\x7e]+/)) {
23909 this.markInvalid(this.errors.PwdBadChar);
23910 this.errorMsg = this.errors.PwdBadChar;
23913 if (value.length < 6) {
23914 this.markInvalid(this.errors.PwdShort);
23915 this.errorMsg = this.errors.PwdShort;
23918 if (value.length > 16) {
23919 this.markInvalid(this.errors.PwdLong);
23920 this.errorMsg = this.errors.PwdLong;
23924 if (this.ClientSideStrongPassword(value)) {
23926 } else if (this.ClientSideMediumPassword(value)) {
23928 } else if (this.ClientSideWeakPassword(value)) {
23935 if (strength < 2) {
23936 //this.markInvalid(this.errors.TooWeak);
23937 this.errorMsg = this.errors.TooWeak;
23942 console.log('strength2: ' + strength);
23944 //var pm = this.trigger.child('div/div/div').dom;
23946 var pm = this.trigger.child('div/div');
23947 pm.removeClass(this.meterClass);
23948 pm.addClass(this.meterClass[strength]);
23950 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23952 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23954 this.errorMsg = '';
23958 CharacterSetChecks: function (type)
23961 this.fResult = false;
23964 isctype: function (character, type)
23967 case this.kCapitalLetter:
23968 if (character >= 'A' && character <= 'Z') {
23973 case this.kSmallLetter:
23974 if (character >= 'a' && character <= 'z') {
23980 if (character >= '0' && character <= '9') {
23985 case this.kPunctuation:
23986 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23997 IsLongEnough: function (pwd, size)
23999 return !(pwd == null || isNaN(size) || pwd.length < size);
24002 SpansEnoughCharacterSets: function (word, nb)
24004 if (!this.IsLongEnough(word, nb))
24009 var characterSetChecks = new Array(
24010 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24011 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24014 for (var index = 0; index < word.length; ++index) {
24015 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24016 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24017 characterSetChecks[nCharSet].fResult = true;
24024 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24025 if (characterSetChecks[nCharSet].fResult) {
24030 if (nCharSets < nb) {
24036 ClientSideStrongPassword: function (pwd)
24038 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24041 ClientSideMediumPassword: function (pwd)
24043 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24046 ClientSideWeakPassword: function (pwd)
24048 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24051 })//<script type="text/javascript">
24054 * Based Ext JS Library 1.1.1
24055 * Copyright(c) 2006-2007, Ext JS, LLC.
24061 * @class Roo.HtmlEditorCore
24062 * @extends Roo.Component
24063 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24065 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24068 Roo.HtmlEditorCore = function(config){
24071 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24076 * @event initialize
24077 * Fires when the editor is fully initialized (including the iframe)
24078 * @param {Roo.HtmlEditorCore} this
24083 * Fires when the editor is first receives the focus. Any insertion must wait
24084 * until after this event.
24085 * @param {Roo.HtmlEditorCore} this
24089 * @event beforesync
24090 * Fires before the textarea is updated with content from the editor iframe. Return false
24091 * to cancel the sync.
24092 * @param {Roo.HtmlEditorCore} this
24093 * @param {String} html
24097 * @event beforepush
24098 * Fires before the iframe editor is updated with content from the textarea. Return false
24099 * to cancel the push.
24100 * @param {Roo.HtmlEditorCore} this
24101 * @param {String} html
24106 * Fires when the textarea is updated with content from the editor iframe.
24107 * @param {Roo.HtmlEditorCore} this
24108 * @param {String} html
24113 * Fires when the iframe editor is updated with content from the textarea.
24114 * @param {Roo.HtmlEditorCore} this
24115 * @param {String} html
24120 * @event editorevent
24121 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24122 * @param {Roo.HtmlEditorCore} this
24128 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24130 // defaults : white / black...
24131 this.applyBlacklists();
24138 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24142 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24148 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24153 * @cfg {Number} height (in pixels)
24157 * @cfg {Number} width (in pixels)
24162 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24165 stylesheets: false,
24170 // private properties
24171 validationEvent : false,
24173 initialized : false,
24175 sourceEditMode : false,
24176 onFocus : Roo.emptyFn,
24178 hideMode:'offsets',
24182 // blacklist + whitelisted elements..
24189 * Protected method that will not generally be called directly. It
24190 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24191 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24193 getDocMarkup : function(){
24197 // inherit styels from page...??
24198 if (this.stylesheets === false) {
24200 Roo.get(document.head).select('style').each(function(node) {
24201 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24204 Roo.get(document.head).select('link').each(function(node) {
24205 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24208 } else if (!this.stylesheets.length) {
24210 st = '<style type="text/css">' +
24211 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24214 for (var i in this.stylesheets) {
24215 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24220 st += '<style type="text/css">' +
24221 'IMG { cursor: pointer } ' +
24224 var cls = 'roo-htmleditor-body';
24226 if(this.bodyCls.length){
24227 cls += ' ' + this.bodyCls;
24230 return '<html><head>' + st +
24231 //<style type="text/css">' +
24232 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24234 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24238 onRender : function(ct, position)
24241 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24242 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24245 this.el.dom.style.border = '0 none';
24246 this.el.dom.setAttribute('tabIndex', -1);
24247 this.el.addClass('x-hidden hide');
24251 if(Roo.isIE){ // fix IE 1px bogus margin
24252 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24256 this.frameId = Roo.id();
24260 var iframe = this.owner.wrap.createChild({
24262 cls: 'form-control', // bootstrap..
24264 name: this.frameId,
24265 frameBorder : 'no',
24266 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24271 this.iframe = iframe.dom;
24273 this.assignDocWin();
24275 this.doc.designMode = 'on';
24278 this.doc.write(this.getDocMarkup());
24282 var task = { // must defer to wait for browser to be ready
24284 //console.log("run task?" + this.doc.readyState);
24285 this.assignDocWin();
24286 if(this.doc.body || this.doc.readyState == 'complete'){
24288 this.doc.designMode="on";
24292 Roo.TaskMgr.stop(task);
24293 this.initEditor.defer(10, this);
24300 Roo.TaskMgr.start(task);
24305 onResize : function(w, h)
24307 Roo.log('resize: ' +w + ',' + h );
24308 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24312 if(typeof w == 'number'){
24314 this.iframe.style.width = w + 'px';
24316 if(typeof h == 'number'){
24318 this.iframe.style.height = h + 'px';
24320 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24327 * Toggles the editor between standard and source edit mode.
24328 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24330 toggleSourceEdit : function(sourceEditMode){
24332 this.sourceEditMode = sourceEditMode === true;
24334 if(this.sourceEditMode){
24336 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24339 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24340 //this.iframe.className = '';
24343 //this.setSize(this.owner.wrap.getSize());
24344 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24351 * Protected method that will not generally be called directly. If you need/want
24352 * custom HTML cleanup, this is the method you should override.
24353 * @param {String} html The HTML to be cleaned
24354 * return {String} The cleaned HTML
24356 cleanHtml : function(html){
24357 html = String(html);
24358 if(html.length > 5){
24359 if(Roo.isSafari){ // strip safari nonsense
24360 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24363 if(html == ' '){
24370 * HTML Editor -> Textarea
24371 * Protected method that will not generally be called directly. Syncs the contents
24372 * of the editor iframe with the textarea.
24374 syncValue : function(){
24375 if(this.initialized){
24376 var bd = (this.doc.body || this.doc.documentElement);
24377 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24378 var html = bd.innerHTML;
24380 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24381 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24383 html = '<div style="'+m[0]+'">' + html + '</div>';
24386 html = this.cleanHtml(html);
24387 // fix up the special chars.. normaly like back quotes in word...
24388 // however we do not want to do this with chinese..
24389 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24391 var cc = match.charCodeAt();
24393 // Get the character value, handling surrogate pairs
24394 if (match.length == 2) {
24395 // It's a surrogate pair, calculate the Unicode code point
24396 var high = match.charCodeAt(0) - 0xD800;
24397 var low = match.charCodeAt(1) - 0xDC00;
24398 cc = (high * 0x400) + low + 0x10000;
24400 (cc >= 0x4E00 && cc < 0xA000 ) ||
24401 (cc >= 0x3400 && cc < 0x4E00 ) ||
24402 (cc >= 0xf900 && cc < 0xfb00 )
24407 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24408 return "&#" + cc + ";";
24415 if(this.owner.fireEvent('beforesync', this, html) !== false){
24416 this.el.dom.value = html;
24417 this.owner.fireEvent('sync', this, html);
24423 * Protected method that will not generally be called directly. Pushes the value of the textarea
24424 * into the iframe editor.
24426 pushValue : function(){
24427 if(this.initialized){
24428 var v = this.el.dom.value.trim();
24430 // if(v.length < 1){
24434 if(this.owner.fireEvent('beforepush', this, v) !== false){
24435 var d = (this.doc.body || this.doc.documentElement);
24437 this.cleanUpPaste();
24438 this.el.dom.value = d.innerHTML;
24439 this.owner.fireEvent('push', this, v);
24445 deferFocus : function(){
24446 this.focus.defer(10, this);
24450 focus : function(){
24451 if(this.win && !this.sourceEditMode){
24458 assignDocWin: function()
24460 var iframe = this.iframe;
24463 this.doc = iframe.contentWindow.document;
24464 this.win = iframe.contentWindow;
24466 // if (!Roo.get(this.frameId)) {
24469 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24470 // this.win = Roo.get(this.frameId).dom.contentWindow;
24472 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24476 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24477 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24482 initEditor : function(){
24483 //console.log("INIT EDITOR");
24484 this.assignDocWin();
24488 this.doc.designMode="on";
24490 this.doc.write(this.getDocMarkup());
24493 var dbody = (this.doc.body || this.doc.documentElement);
24494 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24495 // this copies styles from the containing element into thsi one..
24496 // not sure why we need all of this..
24497 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24499 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24500 //ss['background-attachment'] = 'fixed'; // w3c
24501 dbody.bgProperties = 'fixed'; // ie
24502 //Roo.DomHelper.applyStyles(dbody, ss);
24503 Roo.EventManager.on(this.doc, {
24504 //'mousedown': this.onEditorEvent,
24505 'mouseup': this.onEditorEvent,
24506 'dblclick': this.onEditorEvent,
24507 'click': this.onEditorEvent,
24508 'keyup': this.onEditorEvent,
24513 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24515 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24516 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24518 this.initialized = true;
24520 this.owner.fireEvent('initialize', this);
24525 onDestroy : function(){
24531 //for (var i =0; i < this.toolbars.length;i++) {
24532 // // fixme - ask toolbars for heights?
24533 // this.toolbars[i].onDestroy();
24536 //this.wrap.dom.innerHTML = '';
24537 //this.wrap.remove();
24542 onFirstFocus : function(){
24544 this.assignDocWin();
24547 this.activated = true;
24550 if(Roo.isGecko){ // prevent silly gecko errors
24552 var s = this.win.getSelection();
24553 if(!s.focusNode || s.focusNode.nodeType != 3){
24554 var r = s.getRangeAt(0);
24555 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24560 this.execCmd('useCSS', true);
24561 this.execCmd('styleWithCSS', false);
24564 this.owner.fireEvent('activate', this);
24568 adjustFont: function(btn){
24569 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24570 //if(Roo.isSafari){ // safari
24573 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24574 if(Roo.isSafari){ // safari
24575 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24576 v = (v < 10) ? 10 : v;
24577 v = (v > 48) ? 48 : v;
24578 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24583 v = Math.max(1, v+adjust);
24585 this.execCmd('FontSize', v );
24588 onEditorEvent : function(e)
24590 this.owner.fireEvent('editorevent', this, e);
24591 // this.updateToolbar();
24592 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24595 insertTag : function(tg)
24597 // could be a bit smarter... -> wrap the current selected tRoo..
24598 if (tg.toLowerCase() == 'span' ||
24599 tg.toLowerCase() == 'code' ||
24600 tg.toLowerCase() == 'sup' ||
24601 tg.toLowerCase() == 'sub'
24604 range = this.createRange(this.getSelection());
24605 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24606 wrappingNode.appendChild(range.extractContents());
24607 range.insertNode(wrappingNode);
24614 this.execCmd("formatblock", tg);
24618 insertText : function(txt)
24622 var range = this.createRange();
24623 range.deleteContents();
24624 //alert(Sender.getAttribute('label'));
24626 range.insertNode(this.doc.createTextNode(txt));
24632 * Executes a Midas editor command on the editor document and performs necessary focus and
24633 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24634 * @param {String} cmd The Midas command
24635 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24637 relayCmd : function(cmd, value){
24639 this.execCmd(cmd, value);
24640 this.owner.fireEvent('editorevent', this);
24641 //this.updateToolbar();
24642 this.owner.deferFocus();
24646 * Executes a Midas editor command directly on the editor document.
24647 * For visual commands, you should use {@link #relayCmd} instead.
24648 * <b>This should only be called after the editor is initialized.</b>
24649 * @param {String} cmd The Midas command
24650 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24652 execCmd : function(cmd, value){
24653 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24660 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24662 * @param {String} text | dom node..
24664 insertAtCursor : function(text)
24667 if(!this.activated){
24673 var r = this.doc.selection.createRange();
24684 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24688 // from jquery ui (MIT licenced)
24690 var win = this.win;
24692 if (win.getSelection && win.getSelection().getRangeAt) {
24693 range = win.getSelection().getRangeAt(0);
24694 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24695 range.insertNode(node);
24696 } else if (win.document.selection && win.document.selection.createRange) {
24697 // no firefox support
24698 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24699 win.document.selection.createRange().pasteHTML(txt);
24701 // no firefox support
24702 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24703 this.execCmd('InsertHTML', txt);
24712 mozKeyPress : function(e){
24714 var c = e.getCharCode(), cmd;
24717 c = String.fromCharCode(c).toLowerCase();
24731 this.cleanUpPaste.defer(100, this);
24739 e.preventDefault();
24747 fixKeys : function(){ // load time branching for fastest keydown performance
24749 return function(e){
24750 var k = e.getKey(), r;
24753 r = this.doc.selection.createRange();
24756 r.pasteHTML('    ');
24763 r = this.doc.selection.createRange();
24765 var target = r.parentElement();
24766 if(!target || target.tagName.toLowerCase() != 'li'){
24768 r.pasteHTML('<br />');
24774 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24775 this.cleanUpPaste.defer(100, this);
24781 }else if(Roo.isOpera){
24782 return function(e){
24783 var k = e.getKey();
24787 this.execCmd('InsertHTML','    ');
24790 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24791 this.cleanUpPaste.defer(100, this);
24796 }else if(Roo.isSafari){
24797 return function(e){
24798 var k = e.getKey();
24802 this.execCmd('InsertText','\t');
24806 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24807 this.cleanUpPaste.defer(100, this);
24815 getAllAncestors: function()
24817 var p = this.getSelectedNode();
24820 a.push(p); // push blank onto stack..
24821 p = this.getParentElement();
24825 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24829 a.push(this.doc.body);
24833 lastSelNode : false,
24836 getSelection : function()
24838 this.assignDocWin();
24839 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24842 getSelectedNode: function()
24844 // this may only work on Gecko!!!
24846 // should we cache this!!!!
24851 var range = this.createRange(this.getSelection()).cloneRange();
24854 var parent = range.parentElement();
24856 var testRange = range.duplicate();
24857 testRange.moveToElementText(parent);
24858 if (testRange.inRange(range)) {
24861 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24864 parent = parent.parentElement;
24869 // is ancestor a text element.
24870 var ac = range.commonAncestorContainer;
24871 if (ac.nodeType == 3) {
24872 ac = ac.parentNode;
24875 var ar = ac.childNodes;
24878 var other_nodes = [];
24879 var has_other_nodes = false;
24880 for (var i=0;i<ar.length;i++) {
24881 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24884 // fullly contained node.
24886 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24891 // probably selected..
24892 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24893 other_nodes.push(ar[i]);
24897 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24902 has_other_nodes = true;
24904 if (!nodes.length && other_nodes.length) {
24905 nodes= other_nodes;
24907 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24913 createRange: function(sel)
24915 // this has strange effects when using with
24916 // top toolbar - not sure if it's a great idea.
24917 //this.editor.contentWindow.focus();
24918 if (typeof sel != "undefined") {
24920 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24922 return this.doc.createRange();
24925 return this.doc.createRange();
24928 getParentElement: function()
24931 this.assignDocWin();
24932 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24934 var range = this.createRange(sel);
24937 var p = range.commonAncestorContainer;
24938 while (p.nodeType == 3) { // text node
24949 * Range intersection.. the hard stuff...
24953 * [ -- selected range --- ]
24957 * if end is before start or hits it. fail.
24958 * if start is after end or hits it fail.
24960 * if either hits (but other is outside. - then it's not
24966 // @see http://www.thismuchiknow.co.uk/?p=64.
24967 rangeIntersectsNode : function(range, node)
24969 var nodeRange = node.ownerDocument.createRange();
24971 nodeRange.selectNode(node);
24973 nodeRange.selectNodeContents(node);
24976 var rangeStartRange = range.cloneRange();
24977 rangeStartRange.collapse(true);
24979 var rangeEndRange = range.cloneRange();
24980 rangeEndRange.collapse(false);
24982 var nodeStartRange = nodeRange.cloneRange();
24983 nodeStartRange.collapse(true);
24985 var nodeEndRange = nodeRange.cloneRange();
24986 nodeEndRange.collapse(false);
24988 return rangeStartRange.compareBoundaryPoints(
24989 Range.START_TO_START, nodeEndRange) == -1 &&
24990 rangeEndRange.compareBoundaryPoints(
24991 Range.START_TO_START, nodeStartRange) == 1;
24995 rangeCompareNode : function(range, node)
24997 var nodeRange = node.ownerDocument.createRange();
24999 nodeRange.selectNode(node);
25001 nodeRange.selectNodeContents(node);
25005 range.collapse(true);
25007 nodeRange.collapse(true);
25009 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25010 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25012 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25014 var nodeIsBefore = ss == 1;
25015 var nodeIsAfter = ee == -1;
25017 if (nodeIsBefore && nodeIsAfter) {
25020 if (!nodeIsBefore && nodeIsAfter) {
25021 return 1; //right trailed.
25024 if (nodeIsBefore && !nodeIsAfter) {
25025 return 2; // left trailed.
25031 // private? - in a new class?
25032 cleanUpPaste : function()
25034 // cleans up the whole document..
25035 Roo.log('cleanuppaste');
25037 this.cleanUpChildren(this.doc.body);
25038 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25039 if (clean != this.doc.body.innerHTML) {
25040 this.doc.body.innerHTML = clean;
25045 cleanWordChars : function(input) {// change the chars to hex code
25046 var he = Roo.HtmlEditorCore;
25048 var output = input;
25049 Roo.each(he.swapCodes, function(sw) {
25050 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25052 output = output.replace(swapper, sw[1]);
25059 cleanUpChildren : function (n)
25061 if (!n.childNodes.length) {
25064 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25065 this.cleanUpChild(n.childNodes[i]);
25072 cleanUpChild : function (node)
25075 //console.log(node);
25076 if (node.nodeName == "#text") {
25077 // clean up silly Windows -- stuff?
25080 if (node.nodeName == "#comment") {
25081 node.parentNode.removeChild(node);
25082 // clean up silly Windows -- stuff?
25085 var lcname = node.tagName.toLowerCase();
25086 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25087 // whitelist of tags..
25089 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25091 node.parentNode.removeChild(node);
25096 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25098 // spans with no attributes - just remove them..
25099 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25100 remove_keep_children = true;
25103 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25104 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25106 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25107 // remove_keep_children = true;
25110 if (remove_keep_children) {
25111 this.cleanUpChildren(node);
25112 // inserts everything just before this node...
25113 while (node.childNodes.length) {
25114 var cn = node.childNodes[0];
25115 node.removeChild(cn);
25116 node.parentNode.insertBefore(cn, node);
25118 node.parentNode.removeChild(node);
25122 if (!node.attributes || !node.attributes.length) {
25127 this.cleanUpChildren(node);
25131 function cleanAttr(n,v)
25134 if (v.match(/^\./) || v.match(/^\//)) {
25137 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25140 if (v.match(/^#/)) {
25143 if (v.match(/^\{/)) { // allow template editing.
25146 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25147 node.removeAttribute(n);
25151 var cwhite = this.cwhite;
25152 var cblack = this.cblack;
25154 function cleanStyle(n,v)
25156 if (v.match(/expression/)) { //XSS?? should we even bother..
25157 node.removeAttribute(n);
25161 var parts = v.split(/;/);
25164 Roo.each(parts, function(p) {
25165 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25169 var l = p.split(':').shift().replace(/\s+/g,'');
25170 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25172 if ( cwhite.length && cblack.indexOf(l) > -1) {
25173 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25174 //node.removeAttribute(n);
25178 // only allow 'c whitelisted system attributes'
25179 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25180 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25181 //node.removeAttribute(n);
25191 if (clean.length) {
25192 node.setAttribute(n, clean.join(';'));
25194 node.removeAttribute(n);
25200 for (var i = node.attributes.length-1; i > -1 ; i--) {
25201 var a = node.attributes[i];
25204 if (a.name.toLowerCase().substr(0,2)=='on') {
25205 node.removeAttribute(a.name);
25208 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25209 node.removeAttribute(a.name);
25212 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25213 cleanAttr(a.name,a.value); // fixme..
25216 if (a.name == 'style') {
25217 cleanStyle(a.name,a.value);
25220 /// clean up MS crap..
25221 // tecnically this should be a list of valid class'es..
25224 if (a.name == 'class') {
25225 if (a.value.match(/^Mso/)) {
25226 node.removeAttribute('class');
25229 if (a.value.match(/^body$/)) {
25230 node.removeAttribute('class');
25241 this.cleanUpChildren(node);
25247 * Clean up MS wordisms...
25249 cleanWord : function(node)
25252 this.cleanWord(this.doc.body);
25257 node.nodeName == 'SPAN' &&
25258 !node.hasAttributes() &&
25259 node.childNodes.length == 1 &&
25260 node.firstChild.nodeName == "#text"
25262 var textNode = node.firstChild;
25263 node.removeChild(textNode);
25264 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25265 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25267 node.parentNode.insertBefore(textNode, node);
25268 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25269 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25271 node.parentNode.removeChild(node);
25274 if (node.nodeName == "#text") {
25275 // clean up silly Windows -- stuff?
25278 if (node.nodeName == "#comment") {
25279 node.parentNode.removeChild(node);
25280 // clean up silly Windows -- stuff?
25284 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25285 node.parentNode.removeChild(node);
25288 //Roo.log(node.tagName);
25289 // remove - but keep children..
25290 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25291 //Roo.log('-- removed');
25292 while (node.childNodes.length) {
25293 var cn = node.childNodes[0];
25294 node.removeChild(cn);
25295 node.parentNode.insertBefore(cn, node);
25296 // move node to parent - and clean it..
25297 this.cleanWord(cn);
25299 node.parentNode.removeChild(node);
25300 /// no need to iterate chidlren = it's got none..
25301 //this.iterateChildren(node, this.cleanWord);
25305 if (node.className.length) {
25307 var cn = node.className.split(/\W+/);
25309 Roo.each(cn, function(cls) {
25310 if (cls.match(/Mso[a-zA-Z]+/)) {
25315 node.className = cna.length ? cna.join(' ') : '';
25317 node.removeAttribute("class");
25321 if (node.hasAttribute("lang")) {
25322 node.removeAttribute("lang");
25325 if (node.hasAttribute("style")) {
25327 var styles = node.getAttribute("style").split(";");
25329 Roo.each(styles, function(s) {
25330 if (!s.match(/:/)) {
25333 var kv = s.split(":");
25334 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25337 // what ever is left... we allow.
25340 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25341 if (!nstyle.length) {
25342 node.removeAttribute('style');
25345 this.iterateChildren(node, this.cleanWord);
25351 * iterateChildren of a Node, calling fn each time, using this as the scole..
25352 * @param {DomNode} node node to iterate children of.
25353 * @param {Function} fn method of this class to call on each item.
25355 iterateChildren : function(node, fn)
25357 if (!node.childNodes.length) {
25360 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25361 fn.call(this, node.childNodes[i])
25367 * cleanTableWidths.
25369 * Quite often pasting from word etc.. results in tables with column and widths.
25370 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25373 cleanTableWidths : function(node)
25378 this.cleanTableWidths(this.doc.body);
25383 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25386 Roo.log(node.tagName);
25387 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25388 this.iterateChildren(node, this.cleanTableWidths);
25391 if (node.hasAttribute('width')) {
25392 node.removeAttribute('width');
25396 if (node.hasAttribute("style")) {
25399 var styles = node.getAttribute("style").split(";");
25401 Roo.each(styles, function(s) {
25402 if (!s.match(/:/)) {
25405 var kv = s.split(":");
25406 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25409 // what ever is left... we allow.
25412 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25413 if (!nstyle.length) {
25414 node.removeAttribute('style');
25418 this.iterateChildren(node, this.cleanTableWidths);
25426 domToHTML : function(currentElement, depth, nopadtext) {
25428 depth = depth || 0;
25429 nopadtext = nopadtext || false;
25431 if (!currentElement) {
25432 return this.domToHTML(this.doc.body);
25435 //Roo.log(currentElement);
25437 var allText = false;
25438 var nodeName = currentElement.nodeName;
25439 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25441 if (nodeName == '#text') {
25443 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25448 if (nodeName != 'BODY') {
25451 // Prints the node tagName, such as <A>, <IMG>, etc
25454 for(i = 0; i < currentElement.attributes.length;i++) {
25456 var aname = currentElement.attributes.item(i).name;
25457 if (!currentElement.attributes.item(i).value.length) {
25460 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25463 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25472 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25475 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25480 // Traverse the tree
25482 var currentElementChild = currentElement.childNodes.item(i);
25483 var allText = true;
25484 var innerHTML = '';
25486 while (currentElementChild) {
25487 // Formatting code (indent the tree so it looks nice on the screen)
25488 var nopad = nopadtext;
25489 if (lastnode == 'SPAN') {
25493 if (currentElementChild.nodeName == '#text') {
25494 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25495 toadd = nopadtext ? toadd : toadd.trim();
25496 if (!nopad && toadd.length > 80) {
25497 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25499 innerHTML += toadd;
25502 currentElementChild = currentElement.childNodes.item(i);
25508 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25510 // Recursively traverse the tree structure of the child node
25511 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25512 lastnode = currentElementChild.nodeName;
25514 currentElementChild=currentElement.childNodes.item(i);
25520 // The remaining code is mostly for formatting the tree
25521 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25526 ret+= "</"+tagName+">";
25532 applyBlacklists : function()
25534 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25535 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25539 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25540 if (b.indexOf(tag) > -1) {
25543 this.white.push(tag);
25547 Roo.each(w, function(tag) {
25548 if (b.indexOf(tag) > -1) {
25551 if (this.white.indexOf(tag) > -1) {
25554 this.white.push(tag);
25559 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25560 if (w.indexOf(tag) > -1) {
25563 this.black.push(tag);
25567 Roo.each(b, function(tag) {
25568 if (w.indexOf(tag) > -1) {
25571 if (this.black.indexOf(tag) > -1) {
25574 this.black.push(tag);
25579 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25580 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25584 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25585 if (b.indexOf(tag) > -1) {
25588 this.cwhite.push(tag);
25592 Roo.each(w, function(tag) {
25593 if (b.indexOf(tag) > -1) {
25596 if (this.cwhite.indexOf(tag) > -1) {
25599 this.cwhite.push(tag);
25604 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25605 if (w.indexOf(tag) > -1) {
25608 this.cblack.push(tag);
25612 Roo.each(b, function(tag) {
25613 if (w.indexOf(tag) > -1) {
25616 if (this.cblack.indexOf(tag) > -1) {
25619 this.cblack.push(tag);
25624 setStylesheets : function(stylesheets)
25626 if(typeof(stylesheets) == 'string'){
25627 Roo.get(this.iframe.contentDocument.head).createChild({
25629 rel : 'stylesheet',
25638 Roo.each(stylesheets, function(s) {
25643 Roo.get(_this.iframe.contentDocument.head).createChild({
25645 rel : 'stylesheet',
25654 removeStylesheets : function()
25658 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25663 setStyle : function(style)
25665 Roo.get(this.iframe.contentDocument.head).createChild({
25674 // hide stuff that is not compatible
25688 * @event specialkey
25692 * @cfg {String} fieldClass @hide
25695 * @cfg {String} focusClass @hide
25698 * @cfg {String} autoCreate @hide
25701 * @cfg {String} inputType @hide
25704 * @cfg {String} invalidClass @hide
25707 * @cfg {String} invalidText @hide
25710 * @cfg {String} msgFx @hide
25713 * @cfg {String} validateOnBlur @hide
25717 Roo.HtmlEditorCore.white = [
25718 'area', 'br', 'img', 'input', 'hr', 'wbr',
25720 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25721 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25722 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25723 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25724 'table', 'ul', 'xmp',
25726 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25729 'dir', 'menu', 'ol', 'ul', 'dl',
25735 Roo.HtmlEditorCore.black = [
25736 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25738 'base', 'basefont', 'bgsound', 'blink', 'body',
25739 'frame', 'frameset', 'head', 'html', 'ilayer',
25740 'iframe', 'layer', 'link', 'meta', 'object',
25741 'script', 'style' ,'title', 'xml' // clean later..
25743 Roo.HtmlEditorCore.clean = [
25744 'script', 'style', 'title', 'xml'
25746 Roo.HtmlEditorCore.remove = [
25751 Roo.HtmlEditorCore.ablack = [
25755 Roo.HtmlEditorCore.aclean = [
25756 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25760 Roo.HtmlEditorCore.pwhite= [
25761 'http', 'https', 'mailto'
25764 // white listed style attributes.
25765 Roo.HtmlEditorCore.cwhite= [
25766 // 'text-align', /// default is to allow most things..
25772 // black listed style attributes.
25773 Roo.HtmlEditorCore.cblack= [
25774 // 'font-size' -- this can be set by the project
25778 Roo.HtmlEditorCore.swapCodes =[
25797 * @class Roo.bootstrap.HtmlEditor
25798 * @extends Roo.bootstrap.TextArea
25799 * Bootstrap HtmlEditor class
25802 * Create a new HtmlEditor
25803 * @param {Object} config The config object
25806 Roo.bootstrap.HtmlEditor = function(config){
25807 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25808 if (!this.toolbars) {
25809 this.toolbars = [];
25812 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25815 * @event initialize
25816 * Fires when the editor is fully initialized (including the iframe)
25817 * @param {HtmlEditor} this
25822 * Fires when the editor is first receives the focus. Any insertion must wait
25823 * until after this event.
25824 * @param {HtmlEditor} this
25828 * @event beforesync
25829 * Fires before the textarea is updated with content from the editor iframe. Return false
25830 * to cancel the sync.
25831 * @param {HtmlEditor} this
25832 * @param {String} html
25836 * @event beforepush
25837 * Fires before the iframe editor is updated with content from the textarea. Return false
25838 * to cancel the push.
25839 * @param {HtmlEditor} this
25840 * @param {String} html
25845 * Fires when the textarea is updated with content from the editor iframe.
25846 * @param {HtmlEditor} this
25847 * @param {String} html
25852 * Fires when the iframe editor is updated with content from the textarea.
25853 * @param {HtmlEditor} this
25854 * @param {String} html
25858 * @event editmodechange
25859 * Fires when the editor switches edit modes
25860 * @param {HtmlEditor} this
25861 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25863 editmodechange: true,
25865 * @event editorevent
25866 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25867 * @param {HtmlEditor} this
25871 * @event firstfocus
25872 * Fires when on first focus - needed by toolbars..
25873 * @param {HtmlEditor} this
25878 * Auto save the htmlEditor value as a file into Events
25879 * @param {HtmlEditor} this
25883 * @event savedpreview
25884 * preview the saved version of htmlEditor
25885 * @param {HtmlEditor} this
25892 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25896 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25901 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25906 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25911 * @cfg {Number} height (in pixels)
25915 * @cfg {Number} width (in pixels)
25920 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25923 stylesheets: false,
25928 // private properties
25929 validationEvent : false,
25931 initialized : false,
25934 onFocus : Roo.emptyFn,
25936 hideMode:'offsets',
25938 tbContainer : false,
25942 toolbarContainer :function() {
25943 return this.wrap.select('.x-html-editor-tb',true).first();
25947 * Protected method that will not generally be called directly. It
25948 * is called when the editor creates its toolbar. Override this method if you need to
25949 * add custom toolbar buttons.
25950 * @param {HtmlEditor} editor
25952 createToolbar : function(){
25953 Roo.log('renewing');
25954 Roo.log("create toolbars");
25956 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25957 this.toolbars[0].render(this.toolbarContainer());
25961 // if (!editor.toolbars || !editor.toolbars.length) {
25962 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25965 // for (var i =0 ; i < editor.toolbars.length;i++) {
25966 // editor.toolbars[i] = Roo.factory(
25967 // typeof(editor.toolbars[i]) == 'string' ?
25968 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25969 // Roo.bootstrap.HtmlEditor);
25970 // editor.toolbars[i].init(editor);
25976 onRender : function(ct, position)
25978 // Roo.log("Call onRender: " + this.xtype);
25980 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25982 this.wrap = this.inputEl().wrap({
25983 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25986 this.editorcore.onRender(ct, position);
25988 if (this.resizable) {
25989 this.resizeEl = new Roo.Resizable(this.wrap, {
25993 minHeight : this.height,
25994 height: this.height,
25995 handles : this.resizable,
25998 resize : function(r, w, h) {
25999 _t.onResize(w,h); // -something
26005 this.createToolbar(this);
26008 if(!this.width && this.resizable){
26009 this.setSize(this.wrap.getSize());
26011 if (this.resizeEl) {
26012 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26013 // should trigger onReize..
26019 onResize : function(w, h)
26021 Roo.log('resize: ' +w + ',' + h );
26022 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26026 if(this.inputEl() ){
26027 if(typeof w == 'number'){
26028 var aw = w - this.wrap.getFrameWidth('lr');
26029 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26032 if(typeof h == 'number'){
26033 var tbh = -11; // fixme it needs to tool bar size!
26034 for (var i =0; i < this.toolbars.length;i++) {
26035 // fixme - ask toolbars for heights?
26036 tbh += this.toolbars[i].el.getHeight();
26037 //if (this.toolbars[i].footer) {
26038 // tbh += this.toolbars[i].footer.el.getHeight();
26046 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26047 ah -= 5; // knock a few pixes off for look..
26048 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26052 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26053 this.editorcore.onResize(ew,eh);
26058 * Toggles the editor between standard and source edit mode.
26059 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26061 toggleSourceEdit : function(sourceEditMode)
26063 this.editorcore.toggleSourceEdit(sourceEditMode);
26065 if(this.editorcore.sourceEditMode){
26066 Roo.log('editor - showing textarea');
26069 // Roo.log(this.syncValue());
26071 this.inputEl().removeClass(['hide', 'x-hidden']);
26072 this.inputEl().dom.removeAttribute('tabIndex');
26073 this.inputEl().focus();
26075 Roo.log('editor - hiding textarea');
26077 // Roo.log(this.pushValue());
26080 this.inputEl().addClass(['hide', 'x-hidden']);
26081 this.inputEl().dom.setAttribute('tabIndex', -1);
26082 //this.deferFocus();
26085 if(this.resizable){
26086 this.setSize(this.wrap.getSize());
26089 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26092 // private (for BoxComponent)
26093 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26095 // private (for BoxComponent)
26096 getResizeEl : function(){
26100 // private (for BoxComponent)
26101 getPositionEl : function(){
26106 initEvents : function(){
26107 this.originalValue = this.getValue();
26111 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26114 // markInvalid : Roo.emptyFn,
26116 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26119 // clearInvalid : Roo.emptyFn,
26121 setValue : function(v){
26122 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26123 this.editorcore.pushValue();
26128 deferFocus : function(){
26129 this.focus.defer(10, this);
26133 focus : function(){
26134 this.editorcore.focus();
26140 onDestroy : function(){
26146 for (var i =0; i < this.toolbars.length;i++) {
26147 // fixme - ask toolbars for heights?
26148 this.toolbars[i].onDestroy();
26151 this.wrap.dom.innerHTML = '';
26152 this.wrap.remove();
26157 onFirstFocus : function(){
26158 //Roo.log("onFirstFocus");
26159 this.editorcore.onFirstFocus();
26160 for (var i =0; i < this.toolbars.length;i++) {
26161 this.toolbars[i].onFirstFocus();
26167 syncValue : function()
26169 this.editorcore.syncValue();
26172 pushValue : function()
26174 this.editorcore.pushValue();
26178 // hide stuff that is not compatible
26192 * @event specialkey
26196 * @cfg {String} fieldClass @hide
26199 * @cfg {String} focusClass @hide
26202 * @cfg {String} autoCreate @hide
26205 * @cfg {String} inputType @hide
26209 * @cfg {String} invalidText @hide
26212 * @cfg {String} msgFx @hide
26215 * @cfg {String} validateOnBlur @hide
26224 Roo.namespace('Roo.bootstrap.htmleditor');
26226 * @class Roo.bootstrap.HtmlEditorToolbar1
26232 new Roo.bootstrap.HtmlEditor({
26235 new Roo.bootstrap.HtmlEditorToolbar1({
26236 disable : { fonts: 1 , format: 1, ..., ... , ...],
26242 * @cfg {Object} disable List of elements to disable..
26243 * @cfg {Array} btns List of additional buttons.
26247 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26250 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26253 Roo.apply(this, config);
26255 // default disabled, based on 'good practice'..
26256 this.disable = this.disable || {};
26257 Roo.applyIf(this.disable, {
26260 specialElements : true
26262 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26264 this.editor = config.editor;
26265 this.editorcore = config.editor.editorcore;
26267 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26269 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26270 // dont call parent... till later.
26272 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26277 editorcore : false,
26282 "h1","h2","h3","h4","h5","h6",
26284 "abbr", "acronym", "address", "cite", "samp", "var",
26288 onRender : function(ct, position)
26290 // Roo.log("Call onRender: " + this.xtype);
26292 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26294 this.el.dom.style.marginBottom = '0';
26296 var editorcore = this.editorcore;
26297 var editor= this.editor;
26300 var btn = function(id,cmd , toggle, handler, html){
26302 var event = toggle ? 'toggle' : 'click';
26307 xns: Roo.bootstrap,
26311 enableToggle:toggle !== false,
26313 pressed : toggle ? false : null,
26316 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26317 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26323 // var cb_box = function...
26328 xns: Roo.bootstrap,
26333 xns: Roo.bootstrap,
26337 Roo.each(this.formats, function(f) {
26338 style.menu.items.push({
26340 xns: Roo.bootstrap,
26341 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26346 editorcore.insertTag(this.tagname);
26353 children.push(style);
26355 btn('bold',false,true);
26356 btn('italic',false,true);
26357 btn('align-left', 'justifyleft',true);
26358 btn('align-center', 'justifycenter',true);
26359 btn('align-right' , 'justifyright',true);
26360 btn('link', false, false, function(btn) {
26361 //Roo.log("create link?");
26362 var url = prompt(this.createLinkText, this.defaultLinkValue);
26363 if(url && url != 'http:/'+'/'){
26364 this.editorcore.relayCmd('createlink', url);
26367 btn('list','insertunorderedlist',true);
26368 btn('pencil', false,true, function(btn){
26370 this.toggleSourceEdit(btn.pressed);
26373 if (this.editor.btns.length > 0) {
26374 for (var i = 0; i<this.editor.btns.length; i++) {
26375 children.push(this.editor.btns[i]);
26383 xns: Roo.bootstrap,
26388 xns: Roo.bootstrap,
26393 cog.menu.items.push({
26395 xns: Roo.bootstrap,
26396 html : Clean styles,
26401 editorcore.insertTag(this.tagname);
26410 this.xtype = 'NavSimplebar';
26412 for(var i=0;i< children.length;i++) {
26414 this.buttons.add(this.addxtypeChild(children[i]));
26418 editor.on('editorevent', this.updateToolbar, this);
26420 onBtnClick : function(id)
26422 this.editorcore.relayCmd(id);
26423 this.editorcore.focus();
26427 * Protected method that will not generally be called directly. It triggers
26428 * a toolbar update by reading the markup state of the current selection in the editor.
26430 updateToolbar: function(){
26432 if(!this.editorcore.activated){
26433 this.editor.onFirstFocus(); // is this neeed?
26437 var btns = this.buttons;
26438 var doc = this.editorcore.doc;
26439 btns.get('bold').setActive(doc.queryCommandState('bold'));
26440 btns.get('italic').setActive(doc.queryCommandState('italic'));
26441 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26443 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26444 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26445 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26447 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26448 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26451 var ans = this.editorcore.getAllAncestors();
26452 if (this.formatCombo) {
26455 var store = this.formatCombo.store;
26456 this.formatCombo.setValue("");
26457 for (var i =0; i < ans.length;i++) {
26458 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26460 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26468 // hides menus... - so this cant be on a menu...
26469 Roo.bootstrap.MenuMgr.hideAll();
26471 Roo.bootstrap.MenuMgr.hideAll();
26472 //this.editorsyncValue();
26474 onFirstFocus: function() {
26475 this.buttons.each(function(item){
26479 toggleSourceEdit : function(sourceEditMode){
26482 if(sourceEditMode){
26483 Roo.log("disabling buttons");
26484 this.buttons.each( function(item){
26485 if(item.cmd != 'pencil'){
26491 Roo.log("enabling buttons");
26492 if(this.editorcore.initialized){
26493 this.buttons.each( function(item){
26499 Roo.log("calling toggole on editor");
26500 // tell the editor that it's been pressed..
26501 this.editor.toggleSourceEdit(sourceEditMode);
26515 * @class Roo.bootstrap.Markdown
26516 * @extends Roo.bootstrap.TextArea
26517 * Bootstrap Showdown editable area
26518 * @cfg {string} content
26521 * Create a new Showdown
26524 Roo.bootstrap.Markdown = function(config){
26525 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26529 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26533 initEvents : function()
26536 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26537 this.markdownEl = this.el.createChild({
26538 cls : 'roo-markdown-area'
26540 this.inputEl().addClass('d-none');
26541 if (this.getValue() == '') {
26542 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26545 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26547 this.markdownEl.on('click', this.toggleTextEdit, this);
26548 this.on('blur', this.toggleTextEdit, this);
26549 this.on('specialkey', this.resizeTextArea, this);
26552 toggleTextEdit : function()
26554 var sh = this.markdownEl.getHeight();
26555 this.inputEl().addClass('d-none');
26556 this.markdownEl.addClass('d-none');
26557 if (!this.editing) {
26559 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26560 this.inputEl().removeClass('d-none');
26561 this.inputEl().focus();
26562 this.editing = true;
26565 // show showdown...
26566 this.updateMarkdown();
26567 this.markdownEl.removeClass('d-none');
26568 this.editing = false;
26571 updateMarkdown : function()
26573 if (this.getValue() == '') {
26574 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26578 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26581 resizeTextArea: function () {
26584 Roo.log([sh, this.getValue().split("\n").length * 30]);
26585 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26587 setValue : function(val)
26589 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26590 if (!this.editing) {
26591 this.updateMarkdown();
26597 if (!this.editing) {
26598 this.toggleTextEdit();
26606 * @class Roo.bootstrap.Table.AbstractSelectionModel
26607 * @extends Roo.util.Observable
26608 * Abstract base class for grid SelectionModels. It provides the interface that should be
26609 * implemented by descendant classes. This class should not be directly instantiated.
26612 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26613 this.locked = false;
26614 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26618 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26619 /** @ignore Called by the grid automatically. Do not call directly. */
26620 init : function(grid){
26626 * Locks the selections.
26629 this.locked = true;
26633 * Unlocks the selections.
26635 unlock : function(){
26636 this.locked = false;
26640 * Returns true if the selections are locked.
26641 * @return {Boolean}
26643 isLocked : function(){
26644 return this.locked;
26648 initEvents : function ()
26654 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26655 * @class Roo.bootstrap.Table.RowSelectionModel
26656 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26657 * It supports multiple selections and keyboard selection/navigation.
26659 * @param {Object} config
26662 Roo.bootstrap.Table.RowSelectionModel = function(config){
26663 Roo.apply(this, config);
26664 this.selections = new Roo.util.MixedCollection(false, function(o){
26669 this.lastActive = false;
26673 * @event selectionchange
26674 * Fires when the selection changes
26675 * @param {SelectionModel} this
26677 "selectionchange" : true,
26679 * @event afterselectionchange
26680 * Fires after the selection changes (eg. by key press or clicking)
26681 * @param {SelectionModel} this
26683 "afterselectionchange" : true,
26685 * @event beforerowselect
26686 * Fires when a row is selected being selected, return false to cancel.
26687 * @param {SelectionModel} this
26688 * @param {Number} rowIndex The selected index
26689 * @param {Boolean} keepExisting False if other selections will be cleared
26691 "beforerowselect" : true,
26694 * Fires when a row is selected.
26695 * @param {SelectionModel} this
26696 * @param {Number} rowIndex The selected index
26697 * @param {Roo.data.Record} r The record
26699 "rowselect" : true,
26701 * @event rowdeselect
26702 * Fires when a row is deselected.
26703 * @param {SelectionModel} this
26704 * @param {Number} rowIndex The selected index
26706 "rowdeselect" : true
26708 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26709 this.locked = false;
26712 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26714 * @cfg {Boolean} singleSelect
26715 * True to allow selection of only one row at a time (defaults to false)
26717 singleSelect : false,
26720 initEvents : function()
26723 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26724 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26725 //}else{ // allow click to work like normal
26726 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26728 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26729 this.grid.on("rowclick", this.handleMouseDown, this);
26731 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26732 "up" : function(e){
26734 this.selectPrevious(e.shiftKey);
26735 }else if(this.last !== false && this.lastActive !== false){
26736 var last = this.last;
26737 this.selectRange(this.last, this.lastActive-1);
26738 this.grid.getView().focusRow(this.lastActive);
26739 if(last !== false){
26743 this.selectFirstRow();
26745 this.fireEvent("afterselectionchange", this);
26747 "down" : function(e){
26749 this.selectNext(e.shiftKey);
26750 }else if(this.last !== false && this.lastActive !== false){
26751 var last = this.last;
26752 this.selectRange(this.last, this.lastActive+1);
26753 this.grid.getView().focusRow(this.lastActive);
26754 if(last !== false){
26758 this.selectFirstRow();
26760 this.fireEvent("afterselectionchange", this);
26764 this.grid.store.on('load', function(){
26765 this.selections.clear();
26768 var view = this.grid.view;
26769 view.on("refresh", this.onRefresh, this);
26770 view.on("rowupdated", this.onRowUpdated, this);
26771 view.on("rowremoved", this.onRemove, this);
26776 onRefresh : function()
26778 var ds = this.grid.store, i, v = this.grid.view;
26779 var s = this.selections;
26780 s.each(function(r){
26781 if((i = ds.indexOfId(r.id)) != -1){
26790 onRemove : function(v, index, r){
26791 this.selections.remove(r);
26795 onRowUpdated : function(v, index, r){
26796 if(this.isSelected(r)){
26797 v.onRowSelect(index);
26803 * @param {Array} records The records to select
26804 * @param {Boolean} keepExisting (optional) True to keep existing selections
26806 selectRecords : function(records, keepExisting)
26809 this.clearSelections();
26811 var ds = this.grid.store;
26812 for(var i = 0, len = records.length; i < len; i++){
26813 this.selectRow(ds.indexOf(records[i]), true);
26818 * Gets the number of selected rows.
26821 getCount : function(){
26822 return this.selections.length;
26826 * Selects the first row in the grid.
26828 selectFirstRow : function(){
26833 * Select the last row.
26834 * @param {Boolean} keepExisting (optional) True to keep existing selections
26836 selectLastRow : function(keepExisting){
26837 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26838 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26842 * Selects the row immediately following the last selected row.
26843 * @param {Boolean} keepExisting (optional) True to keep existing selections
26845 selectNext : function(keepExisting)
26847 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26848 this.selectRow(this.last+1, keepExisting);
26849 this.grid.getView().focusRow(this.last);
26854 * Selects the row that precedes the last selected row.
26855 * @param {Boolean} keepExisting (optional) True to keep existing selections
26857 selectPrevious : function(keepExisting){
26859 this.selectRow(this.last-1, keepExisting);
26860 this.grid.getView().focusRow(this.last);
26865 * Returns the selected records
26866 * @return {Array} Array of selected records
26868 getSelections : function(){
26869 return [].concat(this.selections.items);
26873 * Returns the first selected record.
26876 getSelected : function(){
26877 return this.selections.itemAt(0);
26882 * Clears all selections.
26884 clearSelections : function(fast)
26890 var ds = this.grid.store;
26891 var s = this.selections;
26892 s.each(function(r){
26893 this.deselectRow(ds.indexOfId(r.id));
26897 this.selections.clear();
26904 * Selects all rows.
26906 selectAll : function(){
26910 this.selections.clear();
26911 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26912 this.selectRow(i, true);
26917 * Returns True if there is a selection.
26918 * @return {Boolean}
26920 hasSelection : function(){
26921 return this.selections.length > 0;
26925 * Returns True if the specified row is selected.
26926 * @param {Number/Record} record The record or index of the record to check
26927 * @return {Boolean}
26929 isSelected : function(index){
26930 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26931 return (r && this.selections.key(r.id) ? true : false);
26935 * Returns True if the specified record id is selected.
26936 * @param {String} id The id of record to check
26937 * @return {Boolean}
26939 isIdSelected : function(id){
26940 return (this.selections.key(id) ? true : false);
26945 handleMouseDBClick : function(e, t){
26949 handleMouseDown : function(e, t)
26951 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26952 if(this.isLocked() || rowIndex < 0 ){
26955 if(e.shiftKey && this.last !== false){
26956 var last = this.last;
26957 this.selectRange(last, rowIndex, e.ctrlKey);
26958 this.last = last; // reset the last
26962 var isSelected = this.isSelected(rowIndex);
26963 //Roo.log("select row:" + rowIndex);
26965 this.deselectRow(rowIndex);
26967 this.selectRow(rowIndex, true);
26971 if(e.button !== 0 && isSelected){
26972 alert('rowIndex 2: ' + rowIndex);
26973 view.focusRow(rowIndex);
26974 }else if(e.ctrlKey && isSelected){
26975 this.deselectRow(rowIndex);
26976 }else if(!isSelected){
26977 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26978 view.focusRow(rowIndex);
26982 this.fireEvent("afterselectionchange", this);
26985 handleDragableRowClick : function(grid, rowIndex, e)
26987 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26988 this.selectRow(rowIndex, false);
26989 grid.view.focusRow(rowIndex);
26990 this.fireEvent("afterselectionchange", this);
26995 * Selects multiple rows.
26996 * @param {Array} rows Array of the indexes of the row to select
26997 * @param {Boolean} keepExisting (optional) True to keep existing selections
26999 selectRows : function(rows, keepExisting){
27001 this.clearSelections();
27003 for(var i = 0, len = rows.length; i < len; i++){
27004 this.selectRow(rows[i], true);
27009 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27010 * @param {Number} startRow The index of the first row in the range
27011 * @param {Number} endRow The index of the last row in the range
27012 * @param {Boolean} keepExisting (optional) True to retain existing selections
27014 selectRange : function(startRow, endRow, keepExisting){
27019 this.clearSelections();
27021 if(startRow <= endRow){
27022 for(var i = startRow; i <= endRow; i++){
27023 this.selectRow(i, true);
27026 for(var i = startRow; i >= endRow; i--){
27027 this.selectRow(i, true);
27033 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27034 * @param {Number} startRow The index of the first row in the range
27035 * @param {Number} endRow The index of the last row in the range
27037 deselectRange : function(startRow, endRow, preventViewNotify){
27041 for(var i = startRow; i <= endRow; i++){
27042 this.deselectRow(i, preventViewNotify);
27048 * @param {Number} row The index of the row to select
27049 * @param {Boolean} keepExisting (optional) True to keep existing selections
27051 selectRow : function(index, keepExisting, preventViewNotify)
27053 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27056 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27057 if(!keepExisting || this.singleSelect){
27058 this.clearSelections();
27061 var r = this.grid.store.getAt(index);
27062 //console.log('selectRow - record id :' + r.id);
27064 this.selections.add(r);
27065 this.last = this.lastActive = index;
27066 if(!preventViewNotify){
27067 var proxy = new Roo.Element(
27068 this.grid.getRowDom(index)
27070 proxy.addClass('bg-info info');
27072 this.fireEvent("rowselect", this, index, r);
27073 this.fireEvent("selectionchange", this);
27079 * @param {Number} row The index of the row to deselect
27081 deselectRow : function(index, preventViewNotify)
27086 if(this.last == index){
27089 if(this.lastActive == index){
27090 this.lastActive = false;
27093 var r = this.grid.store.getAt(index);
27098 this.selections.remove(r);
27099 //.console.log('deselectRow - record id :' + r.id);
27100 if(!preventViewNotify){
27102 var proxy = new Roo.Element(
27103 this.grid.getRowDom(index)
27105 proxy.removeClass('bg-info info');
27107 this.fireEvent("rowdeselect", this, index);
27108 this.fireEvent("selectionchange", this);
27112 restoreLast : function(){
27114 this.last = this._last;
27119 acceptsNav : function(row, col, cm){
27120 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27124 onEditorKey : function(field, e){
27125 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27130 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27132 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27134 }else if(k == e.ENTER && !e.ctrlKey){
27138 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27140 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27142 }else if(k == e.ESC){
27146 g.startEditing(newCell[0], newCell[1]);
27152 * Ext JS Library 1.1.1
27153 * Copyright(c) 2006-2007, Ext JS, LLC.
27155 * Originally Released Under LGPL - original licence link has changed is not relivant.
27158 * <script type="text/javascript">
27162 * @class Roo.bootstrap.PagingToolbar
27163 * @extends Roo.bootstrap.NavSimplebar
27164 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27166 * Create a new PagingToolbar
27167 * @param {Object} config The config object
27168 * @param {Roo.data.Store} store
27170 Roo.bootstrap.PagingToolbar = function(config)
27172 // old args format still supported... - xtype is prefered..
27173 // created from xtype...
27175 this.ds = config.dataSource;
27177 if (config.store && !this.ds) {
27178 this.store= Roo.factory(config.store, Roo.data);
27179 this.ds = this.store;
27180 this.ds.xmodule = this.xmodule || false;
27183 this.toolbarItems = [];
27184 if (config.items) {
27185 this.toolbarItems = config.items;
27188 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27193 this.bind(this.ds);
27196 if (Roo.bootstrap.version == 4) {
27197 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27199 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27204 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27206 * @cfg {Roo.data.Store} dataSource
27207 * The underlying data store providing the paged data
27210 * @cfg {String/HTMLElement/Element} container
27211 * container The id or element that will contain the toolbar
27214 * @cfg {Boolean} displayInfo
27215 * True to display the displayMsg (defaults to false)
27218 * @cfg {Number} pageSize
27219 * The number of records to display per page (defaults to 20)
27223 * @cfg {String} displayMsg
27224 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27226 displayMsg : 'Displaying {0} - {1} of {2}',
27228 * @cfg {String} emptyMsg
27229 * The message to display when no records are found (defaults to "No data to display")
27231 emptyMsg : 'No data to display',
27233 * Customizable piece of the default paging text (defaults to "Page")
27236 beforePageText : "Page",
27238 * Customizable piece of the default paging text (defaults to "of %0")
27241 afterPageText : "of {0}",
27243 * Customizable piece of the default paging text (defaults to "First Page")
27246 firstText : "First Page",
27248 * Customizable piece of the default paging text (defaults to "Previous Page")
27251 prevText : "Previous Page",
27253 * Customizable piece of the default paging text (defaults to "Next Page")
27256 nextText : "Next Page",
27258 * Customizable piece of the default paging text (defaults to "Last Page")
27261 lastText : "Last Page",
27263 * Customizable piece of the default paging text (defaults to "Refresh")
27266 refreshText : "Refresh",
27270 onRender : function(ct, position)
27272 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27273 this.navgroup.parentId = this.id;
27274 this.navgroup.onRender(this.el, null);
27275 // add the buttons to the navgroup
27277 if(this.displayInfo){
27278 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27279 this.displayEl = this.el.select('.x-paging-info', true).first();
27280 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27281 // this.displayEl = navel.el.select('span',true).first();
27287 Roo.each(_this.buttons, function(e){ // this might need to use render????
27288 Roo.factory(e).render(_this.el);
27292 Roo.each(_this.toolbarItems, function(e) {
27293 _this.navgroup.addItem(e);
27297 this.first = this.navgroup.addItem({
27298 tooltip: this.firstText,
27299 cls: "prev btn-outline-secondary",
27300 html : ' <i class="fa fa-step-backward"></i>',
27302 preventDefault: true,
27303 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27306 this.prev = this.navgroup.addItem({
27307 tooltip: this.prevText,
27308 cls: "prev btn-outline-secondary",
27309 html : ' <i class="fa fa-backward"></i>',
27311 preventDefault: true,
27312 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27314 //this.addSeparator();
27317 var field = this.navgroup.addItem( {
27319 cls : 'x-paging-position btn-outline-secondary',
27321 html : this.beforePageText +
27322 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27323 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27326 this.field = field.el.select('input', true).first();
27327 this.field.on("keydown", this.onPagingKeydown, this);
27328 this.field.on("focus", function(){this.dom.select();});
27331 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27332 //this.field.setHeight(18);
27333 //this.addSeparator();
27334 this.next = this.navgroup.addItem({
27335 tooltip: this.nextText,
27336 cls: "next btn-outline-secondary",
27337 html : ' <i class="fa fa-forward"></i>',
27339 preventDefault: true,
27340 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27342 this.last = this.navgroup.addItem({
27343 tooltip: this.lastText,
27344 html : ' <i class="fa fa-step-forward"></i>',
27345 cls: "next btn-outline-secondary",
27347 preventDefault: true,
27348 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27350 //this.addSeparator();
27351 this.loading = this.navgroup.addItem({
27352 tooltip: this.refreshText,
27353 cls: "btn-outline-secondary",
27354 html : ' <i class="fa fa-refresh"></i>',
27355 preventDefault: true,
27356 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27362 updateInfo : function(){
27363 if(this.displayEl){
27364 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27365 var msg = count == 0 ?
27369 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27371 this.displayEl.update(msg);
27376 onLoad : function(ds, r, o)
27378 this.cursor = o.params && o.params.start ? o.params.start : 0;
27380 var d = this.getPageData(),
27385 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27386 this.field.dom.value = ap;
27387 this.first.setDisabled(ap == 1);
27388 this.prev.setDisabled(ap == 1);
27389 this.next.setDisabled(ap == ps);
27390 this.last.setDisabled(ap == ps);
27391 this.loading.enable();
27396 getPageData : function(){
27397 var total = this.ds.getTotalCount();
27400 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27401 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27406 onLoadError : function(){
27407 this.loading.enable();
27411 onPagingKeydown : function(e){
27412 var k = e.getKey();
27413 var d = this.getPageData();
27415 var v = this.field.dom.value, pageNum;
27416 if(!v || isNaN(pageNum = parseInt(v, 10))){
27417 this.field.dom.value = d.activePage;
27420 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27421 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27424 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))
27426 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27427 this.field.dom.value = pageNum;
27428 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27431 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27433 var v = this.field.dom.value, pageNum;
27434 var increment = (e.shiftKey) ? 10 : 1;
27435 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27438 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27439 this.field.dom.value = d.activePage;
27442 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27444 this.field.dom.value = parseInt(v, 10) + increment;
27445 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27446 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27453 beforeLoad : function(){
27455 this.loading.disable();
27460 onClick : function(which){
27469 ds.load({params:{start: 0, limit: this.pageSize}});
27472 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27475 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27478 var total = ds.getTotalCount();
27479 var extra = total % this.pageSize;
27480 var lastStart = extra ? (total - extra) : total-this.pageSize;
27481 ds.load({params:{start: lastStart, limit: this.pageSize}});
27484 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27490 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27491 * @param {Roo.data.Store} store The data store to unbind
27493 unbind : function(ds){
27494 ds.un("beforeload", this.beforeLoad, this);
27495 ds.un("load", this.onLoad, this);
27496 ds.un("loadexception", this.onLoadError, this);
27497 ds.un("remove", this.updateInfo, this);
27498 ds.un("add", this.updateInfo, this);
27499 this.ds = undefined;
27503 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27504 * @param {Roo.data.Store} store The data store to bind
27506 bind : function(ds){
27507 ds.on("beforeload", this.beforeLoad, this);
27508 ds.on("load", this.onLoad, this);
27509 ds.on("loadexception", this.onLoadError, this);
27510 ds.on("remove", this.updateInfo, this);
27511 ds.on("add", this.updateInfo, this);
27522 * @class Roo.bootstrap.MessageBar
27523 * @extends Roo.bootstrap.Component
27524 * Bootstrap MessageBar class
27525 * @cfg {String} html contents of the MessageBar
27526 * @cfg {String} weight (info | success | warning | danger) default info
27527 * @cfg {String} beforeClass insert the bar before the given class
27528 * @cfg {Boolean} closable (true | false) default false
27529 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27532 * Create a new Element
27533 * @param {Object} config The config object
27536 Roo.bootstrap.MessageBar = function(config){
27537 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27540 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27546 beforeClass: 'bootstrap-sticky-wrap',
27548 getAutoCreate : function(){
27552 cls: 'alert alert-dismissable alert-' + this.weight,
27557 html: this.html || ''
27563 cfg.cls += ' alert-messages-fixed';
27577 onRender : function(ct, position)
27579 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27582 var cfg = Roo.apply({}, this.getAutoCreate());
27586 cfg.cls += ' ' + this.cls;
27589 cfg.style = this.style;
27591 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27593 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27596 this.el.select('>button.close').on('click', this.hide, this);
27602 if (!this.rendered) {
27608 this.fireEvent('show', this);
27614 if (!this.rendered) {
27620 this.fireEvent('hide', this);
27623 update : function()
27625 // var e = this.el.dom.firstChild;
27627 // if(this.closable){
27628 // e = e.nextSibling;
27631 // e.data = this.html || '';
27633 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27649 * @class Roo.bootstrap.Graph
27650 * @extends Roo.bootstrap.Component
27651 * Bootstrap Graph class
27655 @cfg {String} graphtype bar | vbar | pie
27656 @cfg {number} g_x coodinator | centre x (pie)
27657 @cfg {number} g_y coodinator | centre y (pie)
27658 @cfg {number} g_r radius (pie)
27659 @cfg {number} g_height height of the chart (respected by all elements in the set)
27660 @cfg {number} g_width width of the chart (respected by all elements in the set)
27661 @cfg {Object} title The title of the chart
27664 -opts (object) options for the chart
27666 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27667 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27669 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.
27670 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27672 o stretch (boolean)
27674 -opts (object) options for the pie
27677 o startAngle (number)
27678 o endAngle (number)
27682 * Create a new Input
27683 * @param {Object} config The config object
27686 Roo.bootstrap.Graph = function(config){
27687 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27693 * The img click event for the img.
27694 * @param {Roo.EventObject} e
27700 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27711 //g_colors: this.colors,
27718 getAutoCreate : function(){
27729 onRender : function(ct,position){
27732 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27734 if (typeof(Raphael) == 'undefined') {
27735 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27739 this.raphael = Raphael(this.el.dom);
27741 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27742 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27743 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27744 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27746 r.text(160, 10, "Single Series Chart").attr(txtattr);
27747 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27748 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27749 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27751 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27752 r.barchart(330, 10, 300, 220, data1);
27753 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27754 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27757 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27758 // r.barchart(30, 30, 560, 250, xdata, {
27759 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27760 // axis : "0 0 1 1",
27761 // axisxlabels : xdata
27762 // //yvalues : cols,
27765 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27767 // this.load(null,xdata,{
27768 // axis : "0 0 1 1",
27769 // axisxlabels : xdata
27774 load : function(graphtype,xdata,opts)
27776 this.raphael.clear();
27778 graphtype = this.graphtype;
27783 var r = this.raphael,
27784 fin = function () {
27785 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27787 fout = function () {
27788 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27790 pfin = function() {
27791 this.sector.stop();
27792 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27795 this.label[0].stop();
27796 this.label[0].attr({ r: 7.5 });
27797 this.label[1].attr({ "font-weight": 800 });
27800 pfout = function() {
27801 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27804 this.label[0].animate({ r: 5 }, 500, "bounce");
27805 this.label[1].attr({ "font-weight": 400 });
27811 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27814 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27817 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27818 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27820 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27827 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27832 setTitle: function(o)
27837 initEvents: function() {
27840 this.el.on('click', this.onClick, this);
27844 onClick : function(e)
27846 Roo.log('img onclick');
27847 this.fireEvent('click', this, e);
27859 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27862 * @class Roo.bootstrap.dash.NumberBox
27863 * @extends Roo.bootstrap.Component
27864 * Bootstrap NumberBox class
27865 * @cfg {String} headline Box headline
27866 * @cfg {String} content Box content
27867 * @cfg {String} icon Box icon
27868 * @cfg {String} footer Footer text
27869 * @cfg {String} fhref Footer href
27872 * Create a new NumberBox
27873 * @param {Object} config The config object
27877 Roo.bootstrap.dash.NumberBox = function(config){
27878 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27882 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27891 getAutoCreate : function(){
27895 cls : 'small-box ',
27903 cls : 'roo-headline',
27904 html : this.headline
27908 cls : 'roo-content',
27909 html : this.content
27923 cls : 'ion ' + this.icon
27932 cls : 'small-box-footer',
27933 href : this.fhref || '#',
27937 cfg.cn.push(footer);
27944 onRender : function(ct,position){
27945 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27952 setHeadline: function (value)
27954 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27957 setFooter: function (value, href)
27959 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27962 this.el.select('a.small-box-footer',true).first().attr('href', href);
27967 setContent: function (value)
27969 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27972 initEvents: function()
27986 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27989 * @class Roo.bootstrap.dash.TabBox
27990 * @extends Roo.bootstrap.Component
27991 * Bootstrap TabBox class
27992 * @cfg {String} title Title of the TabBox
27993 * @cfg {String} icon Icon of the TabBox
27994 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27995 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27998 * Create a new TabBox
27999 * @param {Object} config The config object
28003 Roo.bootstrap.dash.TabBox = function(config){
28004 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28009 * When a pane is added
28010 * @param {Roo.bootstrap.dash.TabPane} pane
28014 * @event activatepane
28015 * When a pane is activated
28016 * @param {Roo.bootstrap.dash.TabPane} pane
28018 "activatepane" : true
28026 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28031 tabScrollable : false,
28033 getChildContainer : function()
28035 return this.el.select('.tab-content', true).first();
28038 getAutoCreate : function(){
28042 cls: 'pull-left header',
28050 cls: 'fa ' + this.icon
28056 cls: 'nav nav-tabs pull-right',
28062 if(this.tabScrollable){
28069 cls: 'nav nav-tabs pull-right',
28080 cls: 'nav-tabs-custom',
28085 cls: 'tab-content no-padding',
28093 initEvents : function()
28095 //Roo.log('add add pane handler');
28096 this.on('addpane', this.onAddPane, this);
28099 * Updates the box title
28100 * @param {String} html to set the title to.
28102 setTitle : function(value)
28104 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28106 onAddPane : function(pane)
28108 this.panes.push(pane);
28109 //Roo.log('addpane');
28111 // tabs are rendere left to right..
28112 if(!this.showtabs){
28116 var ctr = this.el.select('.nav-tabs', true).first();
28119 var existing = ctr.select('.nav-tab',true);
28120 var qty = existing.getCount();;
28123 var tab = ctr.createChild({
28125 cls : 'nav-tab' + (qty ? '' : ' active'),
28133 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28136 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28138 pane.el.addClass('active');
28143 onTabClick : function(ev,un,ob,pane)
28145 //Roo.log('tab - prev default');
28146 ev.preventDefault();
28149 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28150 pane.tab.addClass('active');
28151 //Roo.log(pane.title);
28152 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28153 // technically we should have a deactivate event.. but maybe add later.
28154 // and it should not de-activate the selected tab...
28155 this.fireEvent('activatepane', pane);
28156 pane.el.addClass('active');
28157 pane.fireEvent('activate');
28162 getActivePane : function()
28165 Roo.each(this.panes, function(p) {
28166 if(p.el.hasClass('active')){
28187 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28189 * @class Roo.bootstrap.TabPane
28190 * @extends Roo.bootstrap.Component
28191 * Bootstrap TabPane class
28192 * @cfg {Boolean} active (false | true) Default false
28193 * @cfg {String} title title of panel
28197 * Create a new TabPane
28198 * @param {Object} config The config object
28201 Roo.bootstrap.dash.TabPane = function(config){
28202 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28208 * When a pane is activated
28209 * @param {Roo.bootstrap.dash.TabPane} pane
28216 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28221 // the tabBox that this is attached to.
28224 getAutoCreate : function()
28232 cfg.cls += ' active';
28237 initEvents : function()
28239 //Roo.log('trigger add pane handler');
28240 this.parent().fireEvent('addpane', this)
28244 * Updates the tab title
28245 * @param {String} html to set the title to.
28247 setTitle: function(str)
28253 this.tab.select('a', true).first().dom.innerHTML = str;
28270 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28273 * @class Roo.bootstrap.menu.Menu
28274 * @extends Roo.bootstrap.Component
28275 * Bootstrap Menu class - container for Menu
28276 * @cfg {String} html Text of the menu
28277 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28278 * @cfg {String} icon Font awesome icon
28279 * @cfg {String} pos Menu align to (top | bottom) default bottom
28283 * Create a new Menu
28284 * @param {Object} config The config object
28288 Roo.bootstrap.menu.Menu = function(config){
28289 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28293 * @event beforeshow
28294 * Fires before this menu is displayed
28295 * @param {Roo.bootstrap.menu.Menu} this
28299 * @event beforehide
28300 * Fires before this menu is hidden
28301 * @param {Roo.bootstrap.menu.Menu} this
28306 * Fires after this menu is displayed
28307 * @param {Roo.bootstrap.menu.Menu} this
28312 * Fires after this menu is hidden
28313 * @param {Roo.bootstrap.menu.Menu} this
28318 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28319 * @param {Roo.bootstrap.menu.Menu} this
28320 * @param {Roo.EventObject} e
28327 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28331 weight : 'default',
28336 getChildContainer : function() {
28337 if(this.isSubMenu){
28341 return this.el.select('ul.dropdown-menu', true).first();
28344 getAutoCreate : function()
28349 cls : 'roo-menu-text',
28357 cls : 'fa ' + this.icon
28368 cls : 'dropdown-button btn btn-' + this.weight,
28373 cls : 'dropdown-toggle btn btn-' + this.weight,
28383 cls : 'dropdown-menu'
28389 if(this.pos == 'top'){
28390 cfg.cls += ' dropup';
28393 if(this.isSubMenu){
28396 cls : 'dropdown-menu'
28403 onRender : function(ct, position)
28405 this.isSubMenu = ct.hasClass('dropdown-submenu');
28407 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28410 initEvents : function()
28412 if(this.isSubMenu){
28416 this.hidden = true;
28418 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28419 this.triggerEl.on('click', this.onTriggerPress, this);
28421 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28422 this.buttonEl.on('click', this.onClick, this);
28428 if(this.isSubMenu){
28432 return this.el.select('ul.dropdown-menu', true).first();
28435 onClick : function(e)
28437 this.fireEvent("click", this, e);
28440 onTriggerPress : function(e)
28442 if (this.isVisible()) {
28449 isVisible : function(){
28450 return !this.hidden;
28455 this.fireEvent("beforeshow", this);
28457 this.hidden = false;
28458 this.el.addClass('open');
28460 Roo.get(document).on("mouseup", this.onMouseUp, this);
28462 this.fireEvent("show", this);
28469 this.fireEvent("beforehide", this);
28471 this.hidden = true;
28472 this.el.removeClass('open');
28474 Roo.get(document).un("mouseup", this.onMouseUp);
28476 this.fireEvent("hide", this);
28479 onMouseUp : function()
28493 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28496 * @class Roo.bootstrap.menu.Item
28497 * @extends Roo.bootstrap.Component
28498 * Bootstrap MenuItem class
28499 * @cfg {Boolean} submenu (true | false) default false
28500 * @cfg {String} html text of the item
28501 * @cfg {String} href the link
28502 * @cfg {Boolean} disable (true | false) default false
28503 * @cfg {Boolean} preventDefault (true | false) default true
28504 * @cfg {String} icon Font awesome icon
28505 * @cfg {String} pos Submenu align to (left | right) default right
28509 * Create a new Item
28510 * @param {Object} config The config object
28514 Roo.bootstrap.menu.Item = function(config){
28515 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28519 * Fires when the mouse is hovering over this menu
28520 * @param {Roo.bootstrap.menu.Item} this
28521 * @param {Roo.EventObject} e
28526 * Fires when the mouse exits this menu
28527 * @param {Roo.bootstrap.menu.Item} this
28528 * @param {Roo.EventObject} e
28534 * The raw click event for the entire grid.
28535 * @param {Roo.EventObject} e
28541 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28546 preventDefault: true,
28551 getAutoCreate : function()
28556 cls : 'roo-menu-item-text',
28564 cls : 'fa ' + this.icon
28573 href : this.href || '#',
28580 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28584 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28586 if(this.pos == 'left'){
28587 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28594 initEvents : function()
28596 this.el.on('mouseover', this.onMouseOver, this);
28597 this.el.on('mouseout', this.onMouseOut, this);
28599 this.el.select('a', true).first().on('click', this.onClick, this);
28603 onClick : function(e)
28605 if(this.preventDefault){
28606 e.preventDefault();
28609 this.fireEvent("click", this, e);
28612 onMouseOver : function(e)
28614 if(this.submenu && this.pos == 'left'){
28615 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28618 this.fireEvent("mouseover", this, e);
28621 onMouseOut : function(e)
28623 this.fireEvent("mouseout", this, e);
28635 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28638 * @class Roo.bootstrap.menu.Separator
28639 * @extends Roo.bootstrap.Component
28640 * Bootstrap Separator class
28643 * Create a new Separator
28644 * @param {Object} config The config object
28648 Roo.bootstrap.menu.Separator = function(config){
28649 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28652 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28654 getAutoCreate : function(){
28675 * @class Roo.bootstrap.Tooltip
28676 * Bootstrap Tooltip class
28677 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28678 * to determine which dom element triggers the tooltip.
28680 * It needs to add support for additional attributes like tooltip-position
28683 * Create a new Toolti
28684 * @param {Object} config The config object
28687 Roo.bootstrap.Tooltip = function(config){
28688 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28690 this.alignment = Roo.bootstrap.Tooltip.alignment;
28692 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28693 this.alignment = config.alignment;
28698 Roo.apply(Roo.bootstrap.Tooltip, {
28700 * @function init initialize tooltip monitoring.
28704 currentTip : false,
28705 currentRegion : false,
28711 Roo.get(document).on('mouseover', this.enter ,this);
28712 Roo.get(document).on('mouseout', this.leave, this);
28715 this.currentTip = new Roo.bootstrap.Tooltip();
28718 enter : function(ev)
28720 var dom = ev.getTarget();
28722 //Roo.log(['enter',dom]);
28723 var el = Roo.fly(dom);
28724 if (this.currentEl) {
28726 //Roo.log(this.currentEl);
28727 //Roo.log(this.currentEl.contains(dom));
28728 if (this.currentEl == el) {
28731 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28737 if (this.currentTip.el) {
28738 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28742 if(!el || el.dom == document){
28748 // you can not look for children, as if el is the body.. then everythign is the child..
28749 if (!el.attr('tooltip')) { //
28750 if (!el.select("[tooltip]").elements.length) {
28753 // is the mouse over this child...?
28754 bindEl = el.select("[tooltip]").first();
28755 var xy = ev.getXY();
28756 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28757 //Roo.log("not in region.");
28760 //Roo.log("child element over..");
28763 this.currentEl = bindEl;
28764 this.currentTip.bind(bindEl);
28765 this.currentRegion = Roo.lib.Region.getRegion(dom);
28766 this.currentTip.enter();
28769 leave : function(ev)
28771 var dom = ev.getTarget();
28772 //Roo.log(['leave',dom]);
28773 if (!this.currentEl) {
28778 if (dom != this.currentEl.dom) {
28781 var xy = ev.getXY();
28782 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28785 // only activate leave if mouse cursor is outside... bounding box..
28790 if (this.currentTip) {
28791 this.currentTip.leave();
28793 //Roo.log('clear currentEl');
28794 this.currentEl = false;
28799 'left' : ['r-l', [-2,0], 'right'],
28800 'right' : ['l-r', [2,0], 'left'],
28801 'bottom' : ['t-b', [0,2], 'top'],
28802 'top' : [ 'b-t', [0,-2], 'bottom']
28808 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28813 delay : null, // can be { show : 300 , hide: 500}
28817 hoverState : null, //???
28819 placement : 'bottom',
28823 getAutoCreate : function(){
28830 cls : 'tooltip-arrow arrow'
28833 cls : 'tooltip-inner'
28840 bind : function(el)
28845 initEvents : function()
28847 this.arrowEl = this.el.select('.arrow', true).first();
28848 this.innerEl = this.el.select('.tooltip-inner', true).first();
28851 enter : function () {
28853 if (this.timeout != null) {
28854 clearTimeout(this.timeout);
28857 this.hoverState = 'in';
28858 //Roo.log("enter - show");
28859 if (!this.delay || !this.delay.show) {
28864 this.timeout = setTimeout(function () {
28865 if (_t.hoverState == 'in') {
28868 }, this.delay.show);
28872 clearTimeout(this.timeout);
28874 this.hoverState = 'out';
28875 if (!this.delay || !this.delay.hide) {
28881 this.timeout = setTimeout(function () {
28882 //Roo.log("leave - timeout");
28884 if (_t.hoverState == 'out') {
28886 Roo.bootstrap.Tooltip.currentEl = false;
28891 show : function (msg)
28894 this.render(document.body);
28897 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28899 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28901 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28903 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28904 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28906 var placement = typeof this.placement == 'function' ?
28907 this.placement.call(this, this.el, on_el) :
28910 var autoToken = /\s?auto?\s?/i;
28911 var autoPlace = autoToken.test(placement);
28913 placement = placement.replace(autoToken, '') || 'top';
28917 //this.el.setXY([0,0]);
28919 //this.el.dom.style.display='block';
28921 //this.el.appendTo(on_el);
28923 var p = this.getPosition();
28924 var box = this.el.getBox();
28930 var align = this.alignment[placement];
28932 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28934 if(placement == 'top' || placement == 'bottom'){
28936 placement = 'right';
28939 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28940 placement = 'left';
28943 var scroll = Roo.select('body', true).first().getScroll();
28945 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28949 align = this.alignment[placement];
28951 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28955 this.el.alignTo(this.bindEl, align[0],align[1]);
28956 //var arrow = this.el.select('.arrow',true).first();
28957 //arrow.set(align[2],
28959 this.el.addClass(placement);
28960 this.el.addClass("bs-tooltip-"+ placement);
28962 this.el.addClass('in fade show');
28964 this.hoverState = null;
28966 if (this.el.hasClass('fade')) {
28981 //this.el.setXY([0,0]);
28982 this.el.removeClass(['show', 'in']);
28998 * @class Roo.bootstrap.LocationPicker
28999 * @extends Roo.bootstrap.Component
29000 * Bootstrap LocationPicker class
29001 * @cfg {Number} latitude Position when init default 0
29002 * @cfg {Number} longitude Position when init default 0
29003 * @cfg {Number} zoom default 15
29004 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29005 * @cfg {Boolean} mapTypeControl default false
29006 * @cfg {Boolean} disableDoubleClickZoom default false
29007 * @cfg {Boolean} scrollwheel default true
29008 * @cfg {Boolean} streetViewControl default false
29009 * @cfg {Number} radius default 0
29010 * @cfg {String} locationName
29011 * @cfg {Boolean} draggable default true
29012 * @cfg {Boolean} enableAutocomplete default false
29013 * @cfg {Boolean} enableReverseGeocode default true
29014 * @cfg {String} markerTitle
29017 * Create a new LocationPicker
29018 * @param {Object} config The config object
29022 Roo.bootstrap.LocationPicker = function(config){
29024 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29029 * Fires when the picker initialized.
29030 * @param {Roo.bootstrap.LocationPicker} this
29031 * @param {Google Location} location
29035 * @event positionchanged
29036 * Fires when the picker position changed.
29037 * @param {Roo.bootstrap.LocationPicker} this
29038 * @param {Google Location} location
29040 positionchanged : true,
29043 * Fires when the map resize.
29044 * @param {Roo.bootstrap.LocationPicker} this
29049 * Fires when the map show.
29050 * @param {Roo.bootstrap.LocationPicker} this
29055 * Fires when the map hide.
29056 * @param {Roo.bootstrap.LocationPicker} this
29061 * Fires when click the map.
29062 * @param {Roo.bootstrap.LocationPicker} this
29063 * @param {Map event} e
29067 * @event mapRightClick
29068 * Fires when right click the map.
29069 * @param {Roo.bootstrap.LocationPicker} this
29070 * @param {Map event} e
29072 mapRightClick : true,
29074 * @event markerClick
29075 * Fires when click the marker.
29076 * @param {Roo.bootstrap.LocationPicker} this
29077 * @param {Map event} e
29079 markerClick : true,
29081 * @event markerRightClick
29082 * Fires when right click the marker.
29083 * @param {Roo.bootstrap.LocationPicker} this
29084 * @param {Map event} e
29086 markerRightClick : true,
29088 * @event OverlayViewDraw
29089 * Fires when OverlayView Draw
29090 * @param {Roo.bootstrap.LocationPicker} this
29092 OverlayViewDraw : true,
29094 * @event OverlayViewOnAdd
29095 * Fires when OverlayView Draw
29096 * @param {Roo.bootstrap.LocationPicker} this
29098 OverlayViewOnAdd : true,
29100 * @event OverlayViewOnRemove
29101 * Fires when OverlayView Draw
29102 * @param {Roo.bootstrap.LocationPicker} this
29104 OverlayViewOnRemove : true,
29106 * @event OverlayViewShow
29107 * Fires when OverlayView Draw
29108 * @param {Roo.bootstrap.LocationPicker} this
29109 * @param {Pixel} cpx
29111 OverlayViewShow : true,
29113 * @event OverlayViewHide
29114 * Fires when OverlayView Draw
29115 * @param {Roo.bootstrap.LocationPicker} this
29117 OverlayViewHide : true,
29119 * @event loadexception
29120 * Fires when load google lib failed.
29121 * @param {Roo.bootstrap.LocationPicker} this
29123 loadexception : true
29128 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29130 gMapContext: false,
29136 mapTypeControl: false,
29137 disableDoubleClickZoom: false,
29139 streetViewControl: false,
29143 enableAutocomplete: false,
29144 enableReverseGeocode: true,
29147 getAutoCreate: function()
29152 cls: 'roo-location-picker'
29158 initEvents: function(ct, position)
29160 if(!this.el.getWidth() || this.isApplied()){
29164 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29169 initial: function()
29171 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29172 this.fireEvent('loadexception', this);
29176 if(!this.mapTypeId){
29177 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29180 this.gMapContext = this.GMapContext();
29182 this.initOverlayView();
29184 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29188 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29189 _this.setPosition(_this.gMapContext.marker.position);
29192 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29193 _this.fireEvent('mapClick', this, event);
29197 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29198 _this.fireEvent('mapRightClick', this, event);
29202 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29203 _this.fireEvent('markerClick', this, event);
29207 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29208 _this.fireEvent('markerRightClick', this, event);
29212 this.setPosition(this.gMapContext.location);
29214 this.fireEvent('initial', this, this.gMapContext.location);
29217 initOverlayView: function()
29221 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29225 _this.fireEvent('OverlayViewDraw', _this);
29230 _this.fireEvent('OverlayViewOnAdd', _this);
29233 onRemove: function()
29235 _this.fireEvent('OverlayViewOnRemove', _this);
29238 show: function(cpx)
29240 _this.fireEvent('OverlayViewShow', _this, cpx);
29245 _this.fireEvent('OverlayViewHide', _this);
29251 fromLatLngToContainerPixel: function(event)
29253 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29256 isApplied: function()
29258 return this.getGmapContext() == false ? false : true;
29261 getGmapContext: function()
29263 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29266 GMapContext: function()
29268 var position = new google.maps.LatLng(this.latitude, this.longitude);
29270 var _map = new google.maps.Map(this.el.dom, {
29273 mapTypeId: this.mapTypeId,
29274 mapTypeControl: this.mapTypeControl,
29275 disableDoubleClickZoom: this.disableDoubleClickZoom,
29276 scrollwheel: this.scrollwheel,
29277 streetViewControl: this.streetViewControl,
29278 locationName: this.locationName,
29279 draggable: this.draggable,
29280 enableAutocomplete: this.enableAutocomplete,
29281 enableReverseGeocode: this.enableReverseGeocode
29284 var _marker = new google.maps.Marker({
29285 position: position,
29287 title: this.markerTitle,
29288 draggable: this.draggable
29295 location: position,
29296 radius: this.radius,
29297 locationName: this.locationName,
29298 addressComponents: {
29299 formatted_address: null,
29300 addressLine1: null,
29301 addressLine2: null,
29303 streetNumber: null,
29307 stateOrProvince: null
29310 domContainer: this.el.dom,
29311 geodecoder: new google.maps.Geocoder()
29315 drawCircle: function(center, radius, options)
29317 if (this.gMapContext.circle != null) {
29318 this.gMapContext.circle.setMap(null);
29322 options = Roo.apply({}, options, {
29323 strokeColor: "#0000FF",
29324 strokeOpacity: .35,
29326 fillColor: "#0000FF",
29330 options.map = this.gMapContext.map;
29331 options.radius = radius;
29332 options.center = center;
29333 this.gMapContext.circle = new google.maps.Circle(options);
29334 return this.gMapContext.circle;
29340 setPosition: function(location)
29342 this.gMapContext.location = location;
29343 this.gMapContext.marker.setPosition(location);
29344 this.gMapContext.map.panTo(location);
29345 this.drawCircle(location, this.gMapContext.radius, {});
29349 if (this.gMapContext.settings.enableReverseGeocode) {
29350 this.gMapContext.geodecoder.geocode({
29351 latLng: this.gMapContext.location
29352 }, function(results, status) {
29354 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29355 _this.gMapContext.locationName = results[0].formatted_address;
29356 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29358 _this.fireEvent('positionchanged', this, location);
29365 this.fireEvent('positionchanged', this, location);
29370 google.maps.event.trigger(this.gMapContext.map, "resize");
29372 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29374 this.fireEvent('resize', this);
29377 setPositionByLatLng: function(latitude, longitude)
29379 this.setPosition(new google.maps.LatLng(latitude, longitude));
29382 getCurrentPosition: function()
29385 latitude: this.gMapContext.location.lat(),
29386 longitude: this.gMapContext.location.lng()
29390 getAddressName: function()
29392 return this.gMapContext.locationName;
29395 getAddressComponents: function()
29397 return this.gMapContext.addressComponents;
29400 address_component_from_google_geocode: function(address_components)
29404 for (var i = 0; i < address_components.length; i++) {
29405 var component = address_components[i];
29406 if (component.types.indexOf("postal_code") >= 0) {
29407 result.postalCode = component.short_name;
29408 } else if (component.types.indexOf("street_number") >= 0) {
29409 result.streetNumber = component.short_name;
29410 } else if (component.types.indexOf("route") >= 0) {
29411 result.streetName = component.short_name;
29412 } else if (component.types.indexOf("neighborhood") >= 0) {
29413 result.city = component.short_name;
29414 } else if (component.types.indexOf("locality") >= 0) {
29415 result.city = component.short_name;
29416 } else if (component.types.indexOf("sublocality") >= 0) {
29417 result.district = component.short_name;
29418 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29419 result.stateOrProvince = component.short_name;
29420 } else if (component.types.indexOf("country") >= 0) {
29421 result.country = component.short_name;
29425 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29426 result.addressLine2 = "";
29430 setZoomLevel: function(zoom)
29432 this.gMapContext.map.setZoom(zoom);
29445 this.fireEvent('show', this);
29456 this.fireEvent('hide', this);
29461 Roo.apply(Roo.bootstrap.LocationPicker, {
29463 OverlayView : function(map, options)
29465 options = options || {};
29472 * @class Roo.bootstrap.Alert
29473 * @extends Roo.bootstrap.Component
29474 * Bootstrap Alert class - shows an alert area box
29476 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29477 Enter a valid email address
29480 * @cfg {String} title The title of alert
29481 * @cfg {String} html The content of alert
29482 * @cfg {String} weight ( success | info | warning | danger )
29483 * @cfg {String} faicon font-awesomeicon
29486 * Create a new alert
29487 * @param {Object} config The config object
29491 Roo.bootstrap.Alert = function(config){
29492 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29496 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29503 getAutoCreate : function()
29512 cls : 'roo-alert-icon'
29517 cls : 'roo-alert-title',
29522 cls : 'roo-alert-text',
29529 cfg.cn[0].cls += ' fa ' + this.faicon;
29533 cfg.cls += ' alert-' + this.weight;
29539 initEvents: function()
29541 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29544 setTitle : function(str)
29546 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29549 setText : function(str)
29551 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29554 setWeight : function(weight)
29557 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29560 this.weight = weight;
29562 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29565 setIcon : function(icon)
29568 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29571 this.faicon = icon;
29573 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29594 * @class Roo.bootstrap.UploadCropbox
29595 * @extends Roo.bootstrap.Component
29596 * Bootstrap UploadCropbox class
29597 * @cfg {String} emptyText show when image has been loaded
29598 * @cfg {String} rotateNotify show when image too small to rotate
29599 * @cfg {Number} errorTimeout default 3000
29600 * @cfg {Number} minWidth default 300
29601 * @cfg {Number} minHeight default 300
29602 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29603 * @cfg {Boolean} isDocument (true|false) default false
29604 * @cfg {String} url action url
29605 * @cfg {String} paramName default 'imageUpload'
29606 * @cfg {String} method default POST
29607 * @cfg {Boolean} loadMask (true|false) default true
29608 * @cfg {Boolean} loadingText default 'Loading...'
29611 * Create a new UploadCropbox
29612 * @param {Object} config The config object
29615 Roo.bootstrap.UploadCropbox = function(config){
29616 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29620 * @event beforeselectfile
29621 * Fire before select file
29622 * @param {Roo.bootstrap.UploadCropbox} this
29624 "beforeselectfile" : true,
29627 * Fire after initEvent
29628 * @param {Roo.bootstrap.UploadCropbox} this
29633 * Fire after initEvent
29634 * @param {Roo.bootstrap.UploadCropbox} this
29635 * @param {String} data
29640 * Fire when preparing the file data
29641 * @param {Roo.bootstrap.UploadCropbox} this
29642 * @param {Object} file
29647 * Fire when get exception
29648 * @param {Roo.bootstrap.UploadCropbox} this
29649 * @param {XMLHttpRequest} xhr
29651 "exception" : true,
29653 * @event beforeloadcanvas
29654 * Fire before load the canvas
29655 * @param {Roo.bootstrap.UploadCropbox} this
29656 * @param {String} src
29658 "beforeloadcanvas" : true,
29661 * Fire when trash image
29662 * @param {Roo.bootstrap.UploadCropbox} this
29667 * Fire when download the image
29668 * @param {Roo.bootstrap.UploadCropbox} this
29672 * @event footerbuttonclick
29673 * Fire when footerbuttonclick
29674 * @param {Roo.bootstrap.UploadCropbox} this
29675 * @param {String} type
29677 "footerbuttonclick" : true,
29681 * @param {Roo.bootstrap.UploadCropbox} this
29686 * Fire when rotate the image
29687 * @param {Roo.bootstrap.UploadCropbox} this
29688 * @param {String} pos
29693 * Fire when inspect the file
29694 * @param {Roo.bootstrap.UploadCropbox} this
29695 * @param {Object} file
29700 * Fire when xhr upload the file
29701 * @param {Roo.bootstrap.UploadCropbox} this
29702 * @param {Object} data
29707 * Fire when arrange the file data
29708 * @param {Roo.bootstrap.UploadCropbox} this
29709 * @param {Object} formData
29714 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29717 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29719 emptyText : 'Click to upload image',
29720 rotateNotify : 'Image is too small to rotate',
29721 errorTimeout : 3000,
29735 cropType : 'image/jpeg',
29737 canvasLoaded : false,
29738 isDocument : false,
29740 paramName : 'imageUpload',
29742 loadingText : 'Loading...',
29745 getAutoCreate : function()
29749 cls : 'roo-upload-cropbox',
29753 cls : 'roo-upload-cropbox-selector',
29758 cls : 'roo-upload-cropbox-body',
29759 style : 'cursor:pointer',
29763 cls : 'roo-upload-cropbox-preview'
29767 cls : 'roo-upload-cropbox-thumb'
29771 cls : 'roo-upload-cropbox-empty-notify',
29772 html : this.emptyText
29776 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29777 html : this.rotateNotify
29783 cls : 'roo-upload-cropbox-footer',
29786 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29796 onRender : function(ct, position)
29798 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29800 if (this.buttons.length) {
29802 Roo.each(this.buttons, function(bb) {
29804 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29806 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29812 this.maskEl = this.el;
29816 initEvents : function()
29818 this.urlAPI = (window.createObjectURL && window) ||
29819 (window.URL && URL.revokeObjectURL && URL) ||
29820 (window.webkitURL && webkitURL);
29822 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29823 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29825 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29826 this.selectorEl.hide();
29828 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29829 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29831 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29832 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29833 this.thumbEl.hide();
29835 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29836 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29838 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29839 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29840 this.errorEl.hide();
29842 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29843 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844 this.footerEl.hide();
29846 this.setThumbBoxSize();
29852 this.fireEvent('initial', this);
29859 window.addEventListener("resize", function() { _this.resize(); } );
29861 this.bodyEl.on('click', this.beforeSelectFile, this);
29864 this.bodyEl.on('touchstart', this.onTouchStart, this);
29865 this.bodyEl.on('touchmove', this.onTouchMove, this);
29866 this.bodyEl.on('touchend', this.onTouchEnd, this);
29870 this.bodyEl.on('mousedown', this.onMouseDown, this);
29871 this.bodyEl.on('mousemove', this.onMouseMove, this);
29872 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29873 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29874 Roo.get(document).on('mouseup', this.onMouseUp, this);
29877 this.selectorEl.on('change', this.onFileSelected, this);
29883 this.baseScale = 1;
29885 this.baseRotate = 1;
29886 this.dragable = false;
29887 this.pinching = false;
29890 this.cropData = false;
29891 this.notifyEl.dom.innerHTML = this.emptyText;
29893 this.selectorEl.dom.value = '';
29897 resize : function()
29899 if(this.fireEvent('resize', this) != false){
29900 this.setThumbBoxPosition();
29901 this.setCanvasPosition();
29905 onFooterButtonClick : function(e, el, o, type)
29908 case 'rotate-left' :
29909 this.onRotateLeft(e);
29911 case 'rotate-right' :
29912 this.onRotateRight(e);
29915 this.beforeSelectFile(e);
29930 this.fireEvent('footerbuttonclick', this, type);
29933 beforeSelectFile : function(e)
29935 e.preventDefault();
29937 if(this.fireEvent('beforeselectfile', this) != false){
29938 this.selectorEl.dom.click();
29942 onFileSelected : function(e)
29944 e.preventDefault();
29946 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29950 var file = this.selectorEl.dom.files[0];
29952 if(this.fireEvent('inspect', this, file) != false){
29953 this.prepare(file);
29958 trash : function(e)
29960 this.fireEvent('trash', this);
29963 download : function(e)
29965 this.fireEvent('download', this);
29968 loadCanvas : function(src)
29970 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29974 this.imageEl = document.createElement('img');
29978 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29980 this.imageEl.src = src;
29984 onLoadCanvas : function()
29986 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29987 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29989 this.bodyEl.un('click', this.beforeSelectFile, this);
29991 this.notifyEl.hide();
29992 this.thumbEl.show();
29993 this.footerEl.show();
29995 this.baseRotateLevel();
29997 if(this.isDocument){
29998 this.setThumbBoxSize();
30001 this.setThumbBoxPosition();
30003 this.baseScaleLevel();
30009 this.canvasLoaded = true;
30012 this.maskEl.unmask();
30017 setCanvasPosition : function()
30019 if(!this.canvasEl){
30023 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30024 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30026 this.previewEl.setLeft(pw);
30027 this.previewEl.setTop(ph);
30031 onMouseDown : function(e)
30035 this.dragable = true;
30036 this.pinching = false;
30038 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30039 this.dragable = false;
30043 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30044 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30048 onMouseMove : function(e)
30052 if(!this.canvasLoaded){
30056 if (!this.dragable){
30060 var minX = Math.ceil(this.thumbEl.getLeft(true));
30061 var minY = Math.ceil(this.thumbEl.getTop(true));
30063 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30064 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30066 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30067 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30069 x = x - this.mouseX;
30070 y = y - this.mouseY;
30072 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30073 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30075 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30076 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30078 this.previewEl.setLeft(bgX);
30079 this.previewEl.setTop(bgY);
30081 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30082 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30085 onMouseUp : function(e)
30089 this.dragable = false;
30092 onMouseWheel : function(e)
30096 this.startScale = this.scale;
30098 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30100 if(!this.zoomable()){
30101 this.scale = this.startScale;
30110 zoomable : function()
30112 var minScale = this.thumbEl.getWidth() / this.minWidth;
30114 if(this.minWidth < this.minHeight){
30115 minScale = this.thumbEl.getHeight() / this.minHeight;
30118 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30119 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30123 (this.rotate == 0 || this.rotate == 180) &&
30125 width > this.imageEl.OriginWidth ||
30126 height > this.imageEl.OriginHeight ||
30127 (width < this.minWidth && height < this.minHeight)
30135 (this.rotate == 90 || this.rotate == 270) &&
30137 width > this.imageEl.OriginWidth ||
30138 height > this.imageEl.OriginHeight ||
30139 (width < this.minHeight && height < this.minWidth)
30146 !this.isDocument &&
30147 (this.rotate == 0 || this.rotate == 180) &&
30149 width < this.minWidth ||
30150 width > this.imageEl.OriginWidth ||
30151 height < this.minHeight ||
30152 height > this.imageEl.OriginHeight
30159 !this.isDocument &&
30160 (this.rotate == 90 || this.rotate == 270) &&
30162 width < this.minHeight ||
30163 width > this.imageEl.OriginWidth ||
30164 height < this.minWidth ||
30165 height > this.imageEl.OriginHeight
30175 onRotateLeft : function(e)
30177 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30179 var minScale = this.thumbEl.getWidth() / this.minWidth;
30181 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30182 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30184 this.startScale = this.scale;
30186 while (this.getScaleLevel() < minScale){
30188 this.scale = this.scale + 1;
30190 if(!this.zoomable()){
30195 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30196 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30201 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30208 this.scale = this.startScale;
30210 this.onRotateFail();
30215 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30217 if(this.isDocument){
30218 this.setThumbBoxSize();
30219 this.setThumbBoxPosition();
30220 this.setCanvasPosition();
30225 this.fireEvent('rotate', this, 'left');
30229 onRotateRight : function(e)
30231 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30233 var minScale = this.thumbEl.getWidth() / this.minWidth;
30235 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30236 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30238 this.startScale = this.scale;
30240 while (this.getScaleLevel() < minScale){
30242 this.scale = this.scale + 1;
30244 if(!this.zoomable()){
30249 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30250 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30255 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30262 this.scale = this.startScale;
30264 this.onRotateFail();
30269 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30271 if(this.isDocument){
30272 this.setThumbBoxSize();
30273 this.setThumbBoxPosition();
30274 this.setCanvasPosition();
30279 this.fireEvent('rotate', this, 'right');
30282 onRotateFail : function()
30284 this.errorEl.show(true);
30288 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30293 this.previewEl.dom.innerHTML = '';
30295 var canvasEl = document.createElement("canvas");
30297 var contextEl = canvasEl.getContext("2d");
30299 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30300 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30301 var center = this.imageEl.OriginWidth / 2;
30303 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30304 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30305 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30306 center = this.imageEl.OriginHeight / 2;
30309 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30311 contextEl.translate(center, center);
30312 contextEl.rotate(this.rotate * Math.PI / 180);
30314 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30316 this.canvasEl = document.createElement("canvas");
30318 this.contextEl = this.canvasEl.getContext("2d");
30320 switch (this.rotate) {
30323 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30324 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30326 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30331 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30332 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30334 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30335 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);
30339 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30344 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30345 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30347 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30348 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);
30352 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);
30357 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30358 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30360 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30361 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30365 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);
30372 this.previewEl.appendChild(this.canvasEl);
30374 this.setCanvasPosition();
30379 if(!this.canvasLoaded){
30383 var imageCanvas = document.createElement("canvas");
30385 var imageContext = imageCanvas.getContext("2d");
30387 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30388 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30390 var center = imageCanvas.width / 2;
30392 imageContext.translate(center, center);
30394 imageContext.rotate(this.rotate * Math.PI / 180);
30396 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30398 var canvas = document.createElement("canvas");
30400 var context = canvas.getContext("2d");
30402 canvas.width = this.minWidth;
30403 canvas.height = this.minHeight;
30405 switch (this.rotate) {
30408 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30409 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30411 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30412 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30414 var targetWidth = this.minWidth - 2 * x;
30415 var targetHeight = this.minHeight - 2 * y;
30419 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30420 scale = targetWidth / width;
30423 if(x > 0 && y == 0){
30424 scale = targetHeight / height;
30427 if(x > 0 && y > 0){
30428 scale = targetWidth / width;
30430 if(width < height){
30431 scale = targetHeight / height;
30435 context.scale(scale, scale);
30437 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30438 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30440 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30441 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30443 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30448 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30449 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30451 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30452 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30454 var targetWidth = this.minWidth - 2 * x;
30455 var targetHeight = this.minHeight - 2 * y;
30459 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30460 scale = targetWidth / width;
30463 if(x > 0 && y == 0){
30464 scale = targetHeight / height;
30467 if(x > 0 && y > 0){
30468 scale = targetWidth / width;
30470 if(width < height){
30471 scale = targetHeight / height;
30475 context.scale(scale, scale);
30477 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30478 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30480 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30481 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30483 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30485 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30490 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30491 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30493 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30494 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30496 var targetWidth = this.minWidth - 2 * x;
30497 var targetHeight = this.minHeight - 2 * y;
30501 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30502 scale = targetWidth / width;
30505 if(x > 0 && y == 0){
30506 scale = targetHeight / height;
30509 if(x > 0 && y > 0){
30510 scale = targetWidth / width;
30512 if(width < height){
30513 scale = targetHeight / height;
30517 context.scale(scale, scale);
30519 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30520 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30522 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30523 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30525 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30526 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30528 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30533 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30534 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30536 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30537 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30539 var targetWidth = this.minWidth - 2 * x;
30540 var targetHeight = this.minHeight - 2 * y;
30544 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30545 scale = targetWidth / width;
30548 if(x > 0 && y == 0){
30549 scale = targetHeight / height;
30552 if(x > 0 && y > 0){
30553 scale = targetWidth / width;
30555 if(width < height){
30556 scale = targetHeight / height;
30560 context.scale(scale, scale);
30562 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30563 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30565 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30566 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30568 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30570 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30577 this.cropData = canvas.toDataURL(this.cropType);
30579 if(this.fireEvent('crop', this, this.cropData) !== false){
30580 this.process(this.file, this.cropData);
30587 setThumbBoxSize : function()
30591 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30592 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30593 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30595 this.minWidth = width;
30596 this.minHeight = height;
30598 if(this.rotate == 90 || this.rotate == 270){
30599 this.minWidth = height;
30600 this.minHeight = width;
30605 width = Math.ceil(this.minWidth * height / this.minHeight);
30607 if(this.minWidth > this.minHeight){
30609 height = Math.ceil(this.minHeight * width / this.minWidth);
30612 this.thumbEl.setStyle({
30613 width : width + 'px',
30614 height : height + 'px'
30621 setThumbBoxPosition : function()
30623 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30624 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30626 this.thumbEl.setLeft(x);
30627 this.thumbEl.setTop(y);
30631 baseRotateLevel : function()
30633 this.baseRotate = 1;
30636 typeof(this.exif) != 'undefined' &&
30637 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30638 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30640 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30643 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30647 baseScaleLevel : function()
30651 if(this.isDocument){
30653 if(this.baseRotate == 6 || this.baseRotate == 8){
30655 height = this.thumbEl.getHeight();
30656 this.baseScale = height / this.imageEl.OriginWidth;
30658 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30659 width = this.thumbEl.getWidth();
30660 this.baseScale = width / this.imageEl.OriginHeight;
30666 height = this.thumbEl.getHeight();
30667 this.baseScale = height / this.imageEl.OriginHeight;
30669 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30670 width = this.thumbEl.getWidth();
30671 this.baseScale = width / this.imageEl.OriginWidth;
30677 if(this.baseRotate == 6 || this.baseRotate == 8){
30679 width = this.thumbEl.getHeight();
30680 this.baseScale = width / this.imageEl.OriginHeight;
30682 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30683 height = this.thumbEl.getWidth();
30684 this.baseScale = height / this.imageEl.OriginHeight;
30687 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30688 height = this.thumbEl.getWidth();
30689 this.baseScale = height / this.imageEl.OriginHeight;
30691 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30692 width = this.thumbEl.getHeight();
30693 this.baseScale = width / this.imageEl.OriginWidth;
30700 width = this.thumbEl.getWidth();
30701 this.baseScale = width / this.imageEl.OriginWidth;
30703 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30704 height = this.thumbEl.getHeight();
30705 this.baseScale = height / this.imageEl.OriginHeight;
30708 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30710 height = this.thumbEl.getHeight();
30711 this.baseScale = height / this.imageEl.OriginHeight;
30713 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30714 width = this.thumbEl.getWidth();
30715 this.baseScale = width / this.imageEl.OriginWidth;
30723 getScaleLevel : function()
30725 return this.baseScale * Math.pow(1.1, this.scale);
30728 onTouchStart : function(e)
30730 if(!this.canvasLoaded){
30731 this.beforeSelectFile(e);
30735 var touches = e.browserEvent.touches;
30741 if(touches.length == 1){
30742 this.onMouseDown(e);
30746 if(touches.length != 2){
30752 for(var i = 0, finger; finger = touches[i]; i++){
30753 coords.push(finger.pageX, finger.pageY);
30756 var x = Math.pow(coords[0] - coords[2], 2);
30757 var y = Math.pow(coords[1] - coords[3], 2);
30759 this.startDistance = Math.sqrt(x + y);
30761 this.startScale = this.scale;
30763 this.pinching = true;
30764 this.dragable = false;
30768 onTouchMove : function(e)
30770 if(!this.pinching && !this.dragable){
30774 var touches = e.browserEvent.touches;
30781 this.onMouseMove(e);
30787 for(var i = 0, finger; finger = touches[i]; i++){
30788 coords.push(finger.pageX, finger.pageY);
30791 var x = Math.pow(coords[0] - coords[2], 2);
30792 var y = Math.pow(coords[1] - coords[3], 2);
30794 this.endDistance = Math.sqrt(x + y);
30796 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30798 if(!this.zoomable()){
30799 this.scale = this.startScale;
30807 onTouchEnd : function(e)
30809 this.pinching = false;
30810 this.dragable = false;
30814 process : function(file, crop)
30817 this.maskEl.mask(this.loadingText);
30820 this.xhr = new XMLHttpRequest();
30822 file.xhr = this.xhr;
30824 this.xhr.open(this.method, this.url, true);
30827 "Accept": "application/json",
30828 "Cache-Control": "no-cache",
30829 "X-Requested-With": "XMLHttpRequest"
30832 for (var headerName in headers) {
30833 var headerValue = headers[headerName];
30835 this.xhr.setRequestHeader(headerName, headerValue);
30841 this.xhr.onload = function()
30843 _this.xhrOnLoad(_this.xhr);
30846 this.xhr.onerror = function()
30848 _this.xhrOnError(_this.xhr);
30851 var formData = new FormData();
30853 formData.append('returnHTML', 'NO');
30856 formData.append('crop', crop);
30859 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30860 formData.append(this.paramName, file, file.name);
30863 if(typeof(file.filename) != 'undefined'){
30864 formData.append('filename', file.filename);
30867 if(typeof(file.mimetype) != 'undefined'){
30868 formData.append('mimetype', file.mimetype);
30871 if(this.fireEvent('arrange', this, formData) != false){
30872 this.xhr.send(formData);
30876 xhrOnLoad : function(xhr)
30879 this.maskEl.unmask();
30882 if (xhr.readyState !== 4) {
30883 this.fireEvent('exception', this, xhr);
30887 var response = Roo.decode(xhr.responseText);
30889 if(!response.success){
30890 this.fireEvent('exception', this, xhr);
30894 var response = Roo.decode(xhr.responseText);
30896 this.fireEvent('upload', this, response);
30900 xhrOnError : function()
30903 this.maskEl.unmask();
30906 Roo.log('xhr on error');
30908 var response = Roo.decode(xhr.responseText);
30914 prepare : function(file)
30917 this.maskEl.mask(this.loadingText);
30923 if(typeof(file) === 'string'){
30924 this.loadCanvas(file);
30928 if(!file || !this.urlAPI){
30933 this.cropType = file.type;
30937 if(this.fireEvent('prepare', this, this.file) != false){
30939 var reader = new FileReader();
30941 reader.onload = function (e) {
30942 if (e.target.error) {
30943 Roo.log(e.target.error);
30947 var buffer = e.target.result,
30948 dataView = new DataView(buffer),
30950 maxOffset = dataView.byteLength - 4,
30954 if (dataView.getUint16(0) === 0xffd8) {
30955 while (offset < maxOffset) {
30956 markerBytes = dataView.getUint16(offset);
30958 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30959 markerLength = dataView.getUint16(offset + 2) + 2;
30960 if (offset + markerLength > dataView.byteLength) {
30961 Roo.log('Invalid meta data: Invalid segment size.');
30965 if(markerBytes == 0xffe1){
30966 _this.parseExifData(
30973 offset += markerLength;
30983 var url = _this.urlAPI.createObjectURL(_this.file);
30985 _this.loadCanvas(url);
30990 reader.readAsArrayBuffer(this.file);
30996 parseExifData : function(dataView, offset, length)
30998 var tiffOffset = offset + 10,
31002 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31003 // No Exif data, might be XMP data instead
31007 // Check for the ASCII code for "Exif" (0x45786966):
31008 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31009 // No Exif data, might be XMP data instead
31012 if (tiffOffset + 8 > dataView.byteLength) {
31013 Roo.log('Invalid Exif data: Invalid segment size.');
31016 // Check for the two null bytes:
31017 if (dataView.getUint16(offset + 8) !== 0x0000) {
31018 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31021 // Check the byte alignment:
31022 switch (dataView.getUint16(tiffOffset)) {
31024 littleEndian = true;
31027 littleEndian = false;
31030 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31033 // Check for the TIFF tag marker (0x002A):
31034 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31035 Roo.log('Invalid Exif data: Missing TIFF marker.');
31038 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31039 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31041 this.parseExifTags(
31044 tiffOffset + dirOffset,
31049 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31054 if (dirOffset + 6 > dataView.byteLength) {
31055 Roo.log('Invalid Exif data: Invalid directory offset.');
31058 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31059 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31060 if (dirEndOffset + 4 > dataView.byteLength) {
31061 Roo.log('Invalid Exif data: Invalid directory size.');
31064 for (i = 0; i < tagsNumber; i += 1) {
31068 dirOffset + 2 + 12 * i, // tag offset
31072 // Return the offset to the next directory:
31073 return dataView.getUint32(dirEndOffset, littleEndian);
31076 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31078 var tag = dataView.getUint16(offset, littleEndian);
31080 this.exif[tag] = this.getExifValue(
31084 dataView.getUint16(offset + 2, littleEndian), // tag type
31085 dataView.getUint32(offset + 4, littleEndian), // tag length
31090 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31092 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31101 Roo.log('Invalid Exif data: Invalid tag type.');
31105 tagSize = tagType.size * length;
31106 // Determine if the value is contained in the dataOffset bytes,
31107 // or if the value at the dataOffset is a pointer to the actual data:
31108 dataOffset = tagSize > 4 ?
31109 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31110 if (dataOffset + tagSize > dataView.byteLength) {
31111 Roo.log('Invalid Exif data: Invalid data offset.');
31114 if (length === 1) {
31115 return tagType.getValue(dataView, dataOffset, littleEndian);
31118 for (i = 0; i < length; i += 1) {
31119 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31122 if (tagType.ascii) {
31124 // Concatenate the chars:
31125 for (i = 0; i < values.length; i += 1) {
31127 // Ignore the terminating NULL byte(s):
31128 if (c === '\u0000') {
31140 Roo.apply(Roo.bootstrap.UploadCropbox, {
31142 'Orientation': 0x0112
31146 1: 0, //'top-left',
31148 3: 180, //'bottom-right',
31149 // 4: 'bottom-left',
31151 6: 90, //'right-top',
31152 // 7: 'right-bottom',
31153 8: 270 //'left-bottom'
31157 // byte, 8-bit unsigned int:
31159 getValue: function (dataView, dataOffset) {
31160 return dataView.getUint8(dataOffset);
31164 // ascii, 8-bit byte:
31166 getValue: function (dataView, dataOffset) {
31167 return String.fromCharCode(dataView.getUint8(dataOffset));
31172 // short, 16 bit int:
31174 getValue: function (dataView, dataOffset, littleEndian) {
31175 return dataView.getUint16(dataOffset, littleEndian);
31179 // long, 32 bit int:
31181 getValue: function (dataView, dataOffset, littleEndian) {
31182 return dataView.getUint32(dataOffset, littleEndian);
31186 // rational = two long values, first is numerator, second is denominator:
31188 getValue: function (dataView, dataOffset, littleEndian) {
31189 return dataView.getUint32(dataOffset, littleEndian) /
31190 dataView.getUint32(dataOffset + 4, littleEndian);
31194 // slong, 32 bit signed int:
31196 getValue: function (dataView, dataOffset, littleEndian) {
31197 return dataView.getInt32(dataOffset, littleEndian);
31201 // srational, two slongs, first is numerator, second is denominator:
31203 getValue: function (dataView, dataOffset, littleEndian) {
31204 return dataView.getInt32(dataOffset, littleEndian) /
31205 dataView.getInt32(dataOffset + 4, littleEndian);
31215 cls : 'btn-group roo-upload-cropbox-rotate-left',
31216 action : 'rotate-left',
31220 cls : 'btn btn-default',
31221 html : '<i class="fa fa-undo"></i>'
31227 cls : 'btn-group roo-upload-cropbox-picture',
31228 action : 'picture',
31232 cls : 'btn btn-default',
31233 html : '<i class="fa fa-picture-o"></i>'
31239 cls : 'btn-group roo-upload-cropbox-rotate-right',
31240 action : 'rotate-right',
31244 cls : 'btn btn-default',
31245 html : '<i class="fa fa-repeat"></i>'
31253 cls : 'btn-group roo-upload-cropbox-rotate-left',
31254 action : 'rotate-left',
31258 cls : 'btn btn-default',
31259 html : '<i class="fa fa-undo"></i>'
31265 cls : 'btn-group roo-upload-cropbox-download',
31266 action : 'download',
31270 cls : 'btn btn-default',
31271 html : '<i class="fa fa-download"></i>'
31277 cls : 'btn-group roo-upload-cropbox-crop',
31282 cls : 'btn btn-default',
31283 html : '<i class="fa fa-crop"></i>'
31289 cls : 'btn-group roo-upload-cropbox-trash',
31294 cls : 'btn btn-default',
31295 html : '<i class="fa fa-trash"></i>'
31301 cls : 'btn-group roo-upload-cropbox-rotate-right',
31302 action : 'rotate-right',
31306 cls : 'btn btn-default',
31307 html : '<i class="fa fa-repeat"></i>'
31315 cls : 'btn-group roo-upload-cropbox-rotate-left',
31316 action : 'rotate-left',
31320 cls : 'btn btn-default',
31321 html : '<i class="fa fa-undo"></i>'
31327 cls : 'btn-group roo-upload-cropbox-rotate-right',
31328 action : 'rotate-right',
31332 cls : 'btn btn-default',
31333 html : '<i class="fa fa-repeat"></i>'
31346 * @class Roo.bootstrap.DocumentManager
31347 * @extends Roo.bootstrap.Component
31348 * Bootstrap DocumentManager class
31349 * @cfg {String} paramName default 'imageUpload'
31350 * @cfg {String} toolTipName default 'filename'
31351 * @cfg {String} method default POST
31352 * @cfg {String} url action url
31353 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31354 * @cfg {Boolean} multiple multiple upload default true
31355 * @cfg {Number} thumbSize default 300
31356 * @cfg {String} fieldLabel
31357 * @cfg {Number} labelWidth default 4
31358 * @cfg {String} labelAlign (left|top) default left
31359 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31360 * @cfg {Number} labellg set the width of label (1-12)
31361 * @cfg {Number} labelmd set the width of label (1-12)
31362 * @cfg {Number} labelsm set the width of label (1-12)
31363 * @cfg {Number} labelxs set the width of label (1-12)
31366 * Create a new DocumentManager
31367 * @param {Object} config The config object
31370 Roo.bootstrap.DocumentManager = function(config){
31371 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31374 this.delegates = [];
31379 * Fire when initial the DocumentManager
31380 * @param {Roo.bootstrap.DocumentManager} this
31385 * inspect selected file
31386 * @param {Roo.bootstrap.DocumentManager} this
31387 * @param {File} file
31392 * Fire when xhr load exception
31393 * @param {Roo.bootstrap.DocumentManager} this
31394 * @param {XMLHttpRequest} xhr
31396 "exception" : true,
31398 * @event afterupload
31399 * Fire when xhr load exception
31400 * @param {Roo.bootstrap.DocumentManager} this
31401 * @param {XMLHttpRequest} xhr
31403 "afterupload" : true,
31406 * prepare the form data
31407 * @param {Roo.bootstrap.DocumentManager} this
31408 * @param {Object} formData
31413 * Fire when remove the file
31414 * @param {Roo.bootstrap.DocumentManager} this
31415 * @param {Object} file
31420 * Fire after refresh the file
31421 * @param {Roo.bootstrap.DocumentManager} this
31426 * Fire after click the image
31427 * @param {Roo.bootstrap.DocumentManager} this
31428 * @param {Object} file
31433 * Fire when upload a image and editable set to true
31434 * @param {Roo.bootstrap.DocumentManager} this
31435 * @param {Object} file
31439 * @event beforeselectfile
31440 * Fire before select file
31441 * @param {Roo.bootstrap.DocumentManager} this
31443 "beforeselectfile" : true,
31446 * Fire before process file
31447 * @param {Roo.bootstrap.DocumentManager} this
31448 * @param {Object} file
31452 * @event previewrendered
31453 * Fire when preview rendered
31454 * @param {Roo.bootstrap.DocumentManager} this
31455 * @param {Object} file
31457 "previewrendered" : true,
31460 "previewResize" : true
31465 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31474 paramName : 'imageUpload',
31475 toolTipName : 'filename',
31478 labelAlign : 'left',
31488 getAutoCreate : function()
31490 var managerWidget = {
31492 cls : 'roo-document-manager',
31496 cls : 'roo-document-manager-selector',
31501 cls : 'roo-document-manager-uploader',
31505 cls : 'roo-document-manager-upload-btn',
31506 html : '<i class="fa fa-plus"></i>'
31517 cls : 'column col-md-12',
31522 if(this.fieldLabel.length){
31527 cls : 'column col-md-12',
31528 html : this.fieldLabel
31532 cls : 'column col-md-12',
31537 if(this.labelAlign == 'left'){
31542 html : this.fieldLabel
31551 if(this.labelWidth > 12){
31552 content[0].style = "width: " + this.labelWidth + 'px';
31555 if(this.labelWidth < 13 && this.labelmd == 0){
31556 this.labelmd = this.labelWidth;
31559 if(this.labellg > 0){
31560 content[0].cls += ' col-lg-' + this.labellg;
31561 content[1].cls += ' col-lg-' + (12 - this.labellg);
31564 if(this.labelmd > 0){
31565 content[0].cls += ' col-md-' + this.labelmd;
31566 content[1].cls += ' col-md-' + (12 - this.labelmd);
31569 if(this.labelsm > 0){
31570 content[0].cls += ' col-sm-' + this.labelsm;
31571 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31574 if(this.labelxs > 0){
31575 content[0].cls += ' col-xs-' + this.labelxs;
31576 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31584 cls : 'row clearfix',
31592 initEvents : function()
31594 this.managerEl = this.el.select('.roo-document-manager', true).first();
31595 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31597 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31598 this.selectorEl.hide();
31601 this.selectorEl.attr('multiple', 'multiple');
31604 this.selectorEl.on('change', this.onFileSelected, this);
31606 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31607 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31609 this.uploader.on('click', this.onUploaderClick, this);
31611 this.renderProgressDialog();
31615 window.addEventListener("resize", function() { _this.refresh(); } );
31617 this.fireEvent('initial', this);
31620 renderProgressDialog : function()
31624 this.progressDialog = new Roo.bootstrap.Modal({
31625 cls : 'roo-document-manager-progress-dialog',
31626 allow_close : false,
31637 btnclick : function() {
31638 _this.uploadCancel();
31644 this.progressDialog.render(Roo.get(document.body));
31646 this.progress = new Roo.bootstrap.Progress({
31647 cls : 'roo-document-manager-progress',
31652 this.progress.render(this.progressDialog.getChildContainer());
31654 this.progressBar = new Roo.bootstrap.ProgressBar({
31655 cls : 'roo-document-manager-progress-bar',
31658 aria_valuemax : 12,
31662 this.progressBar.render(this.progress.getChildContainer());
31665 onUploaderClick : function(e)
31667 e.preventDefault();
31669 if(this.fireEvent('beforeselectfile', this) != false){
31670 this.selectorEl.dom.click();
31675 onFileSelected : function(e)
31677 e.preventDefault();
31679 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31683 Roo.each(this.selectorEl.dom.files, function(file){
31684 if(this.fireEvent('inspect', this, file) != false){
31685 this.files.push(file);
31695 this.selectorEl.dom.value = '';
31697 if(!this.files || !this.files.length){
31701 if(this.boxes > 0 && this.files.length > this.boxes){
31702 this.files = this.files.slice(0, this.boxes);
31705 this.uploader.show();
31707 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31708 this.uploader.hide();
31717 Roo.each(this.files, function(file){
31719 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31720 var f = this.renderPreview(file);
31725 if(file.type.indexOf('image') != -1){
31726 this.delegates.push(
31728 _this.process(file);
31729 }).createDelegate(this)
31737 _this.process(file);
31738 }).createDelegate(this)
31743 this.files = files;
31745 this.delegates = this.delegates.concat(docs);
31747 if(!this.delegates.length){
31752 this.progressBar.aria_valuemax = this.delegates.length;
31759 arrange : function()
31761 if(!this.delegates.length){
31762 this.progressDialog.hide();
31767 var delegate = this.delegates.shift();
31769 this.progressDialog.show();
31771 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31773 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31778 refresh : function()
31780 this.uploader.show();
31782 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31783 this.uploader.hide();
31786 Roo.isTouch ? this.closable(false) : this.closable(true);
31788 this.fireEvent('refresh', this);
31791 onRemove : function(e, el, o)
31793 e.preventDefault();
31795 this.fireEvent('remove', this, o);
31799 remove : function(o)
31803 Roo.each(this.files, function(file){
31804 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31813 this.files = files;
31820 Roo.each(this.files, function(file){
31825 file.target.remove();
31834 onClick : function(e, el, o)
31836 e.preventDefault();
31838 this.fireEvent('click', this, o);
31842 closable : function(closable)
31844 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31846 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31858 xhrOnLoad : function(xhr)
31860 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31864 if (xhr.readyState !== 4) {
31866 this.fireEvent('exception', this, xhr);
31870 var response = Roo.decode(xhr.responseText);
31872 if(!response.success){
31874 this.fireEvent('exception', this, xhr);
31878 var file = this.renderPreview(response.data);
31880 this.files.push(file);
31884 this.fireEvent('afterupload', this, xhr);
31888 xhrOnError : function(xhr)
31890 Roo.log('xhr on error');
31892 var response = Roo.decode(xhr.responseText);
31899 process : function(file)
31901 if(this.fireEvent('process', this, file) !== false){
31902 if(this.editable && file.type.indexOf('image') != -1){
31903 this.fireEvent('edit', this, file);
31907 this.uploadStart(file, false);
31914 uploadStart : function(file, crop)
31916 this.xhr = new XMLHttpRequest();
31918 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31923 file.xhr = this.xhr;
31925 this.managerEl.createChild({
31927 cls : 'roo-document-manager-loading',
31931 tooltip : file.name,
31932 cls : 'roo-document-manager-thumb',
31933 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31939 this.xhr.open(this.method, this.url, true);
31942 "Accept": "application/json",
31943 "Cache-Control": "no-cache",
31944 "X-Requested-With": "XMLHttpRequest"
31947 for (var headerName in headers) {
31948 var headerValue = headers[headerName];
31950 this.xhr.setRequestHeader(headerName, headerValue);
31956 this.xhr.onload = function()
31958 _this.xhrOnLoad(_this.xhr);
31961 this.xhr.onerror = function()
31963 _this.xhrOnError(_this.xhr);
31966 var formData = new FormData();
31968 formData.append('returnHTML', 'NO');
31971 formData.append('crop', crop);
31974 formData.append(this.paramName, file, file.name);
31981 if(this.fireEvent('prepare', this, formData, options) != false){
31983 if(options.manually){
31987 this.xhr.send(formData);
31991 this.uploadCancel();
31994 uploadCancel : function()
32000 this.delegates = [];
32002 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32009 renderPreview : function(file)
32011 if(typeof(file.target) != 'undefined' && file.target){
32015 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32017 var previewEl = this.managerEl.createChild({
32019 cls : 'roo-document-manager-preview',
32023 tooltip : file[this.toolTipName],
32024 cls : 'roo-document-manager-thumb',
32025 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32030 html : '<i class="fa fa-times-circle"></i>'
32035 var close = previewEl.select('button.close', true).first();
32037 close.on('click', this.onRemove, this, file);
32039 file.target = previewEl;
32041 var image = previewEl.select('img', true).first();
32045 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32047 image.on('click', this.onClick, this, file);
32049 this.fireEvent('previewrendered', this, file);
32055 onPreviewLoad : function(file, image)
32057 if(typeof(file.target) == 'undefined' || !file.target){
32061 var width = image.dom.naturalWidth || image.dom.width;
32062 var height = image.dom.naturalHeight || image.dom.height;
32064 if(!this.previewResize) {
32068 if(width > height){
32069 file.target.addClass('wide');
32073 file.target.addClass('tall');
32078 uploadFromSource : function(file, crop)
32080 this.xhr = new XMLHttpRequest();
32082 this.managerEl.createChild({
32084 cls : 'roo-document-manager-loading',
32088 tooltip : file.name,
32089 cls : 'roo-document-manager-thumb',
32090 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32096 this.xhr.open(this.method, this.url, true);
32099 "Accept": "application/json",
32100 "Cache-Control": "no-cache",
32101 "X-Requested-With": "XMLHttpRequest"
32104 for (var headerName in headers) {
32105 var headerValue = headers[headerName];
32107 this.xhr.setRequestHeader(headerName, headerValue);
32113 this.xhr.onload = function()
32115 _this.xhrOnLoad(_this.xhr);
32118 this.xhr.onerror = function()
32120 _this.xhrOnError(_this.xhr);
32123 var formData = new FormData();
32125 formData.append('returnHTML', 'NO');
32127 formData.append('crop', crop);
32129 if(typeof(file.filename) != 'undefined'){
32130 formData.append('filename', file.filename);
32133 if(typeof(file.mimetype) != 'undefined'){
32134 formData.append('mimetype', file.mimetype);
32139 if(this.fireEvent('prepare', this, formData) != false){
32140 this.xhr.send(formData);
32150 * @class Roo.bootstrap.DocumentViewer
32151 * @extends Roo.bootstrap.Component
32152 * Bootstrap DocumentViewer class
32153 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32154 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32157 * Create a new DocumentViewer
32158 * @param {Object} config The config object
32161 Roo.bootstrap.DocumentViewer = function(config){
32162 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32167 * Fire after initEvent
32168 * @param {Roo.bootstrap.DocumentViewer} this
32174 * @param {Roo.bootstrap.DocumentViewer} this
32179 * Fire after download button
32180 * @param {Roo.bootstrap.DocumentViewer} this
32185 * Fire after trash button
32186 * @param {Roo.bootstrap.DocumentViewer} this
32193 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32195 showDownload : true,
32199 getAutoCreate : function()
32203 cls : 'roo-document-viewer',
32207 cls : 'roo-document-viewer-body',
32211 cls : 'roo-document-viewer-thumb',
32215 cls : 'roo-document-viewer-image'
32223 cls : 'roo-document-viewer-footer',
32226 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32230 cls : 'btn-group roo-document-viewer-download',
32234 cls : 'btn btn-default',
32235 html : '<i class="fa fa-download"></i>'
32241 cls : 'btn-group roo-document-viewer-trash',
32245 cls : 'btn btn-default',
32246 html : '<i class="fa fa-trash"></i>'
32259 initEvents : function()
32261 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32262 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32264 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32265 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32267 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32268 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32270 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32271 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32273 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32274 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32276 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32277 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32279 this.bodyEl.on('click', this.onClick, this);
32280 this.downloadBtn.on('click', this.onDownload, this);
32281 this.trashBtn.on('click', this.onTrash, this);
32283 this.downloadBtn.hide();
32284 this.trashBtn.hide();
32286 if(this.showDownload){
32287 this.downloadBtn.show();
32290 if(this.showTrash){
32291 this.trashBtn.show();
32294 if(!this.showDownload && !this.showTrash) {
32295 this.footerEl.hide();
32300 initial : function()
32302 this.fireEvent('initial', this);
32306 onClick : function(e)
32308 e.preventDefault();
32310 this.fireEvent('click', this);
32313 onDownload : function(e)
32315 e.preventDefault();
32317 this.fireEvent('download', this);
32320 onTrash : function(e)
32322 e.preventDefault();
32324 this.fireEvent('trash', this);
32336 * @class Roo.bootstrap.NavProgressBar
32337 * @extends Roo.bootstrap.Component
32338 * Bootstrap NavProgressBar class
32341 * Create a new nav progress bar
32342 * @param {Object} config The config object
32345 Roo.bootstrap.NavProgressBar = function(config){
32346 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32348 this.bullets = this.bullets || [];
32350 // Roo.bootstrap.NavProgressBar.register(this);
32354 * Fires when the active item changes
32355 * @param {Roo.bootstrap.NavProgressBar} this
32356 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32357 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32364 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32369 getAutoCreate : function()
32371 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32375 cls : 'roo-navigation-bar-group',
32379 cls : 'roo-navigation-top-bar'
32383 cls : 'roo-navigation-bullets-bar',
32387 cls : 'roo-navigation-bar'
32394 cls : 'roo-navigation-bottom-bar'
32404 initEvents: function()
32409 onRender : function(ct, position)
32411 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32413 if(this.bullets.length){
32414 Roo.each(this.bullets, function(b){
32423 addItem : function(cfg)
32425 var item = new Roo.bootstrap.NavProgressItem(cfg);
32427 item.parentId = this.id;
32428 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32431 var top = new Roo.bootstrap.Element({
32433 cls : 'roo-navigation-bar-text'
32436 var bottom = new Roo.bootstrap.Element({
32438 cls : 'roo-navigation-bar-text'
32441 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32442 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32444 var topText = new Roo.bootstrap.Element({
32446 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32449 var bottomText = new Roo.bootstrap.Element({
32451 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32454 topText.onRender(top.el, null);
32455 bottomText.onRender(bottom.el, null);
32458 item.bottomEl = bottom;
32461 this.barItems.push(item);
32466 getActive : function()
32468 var active = false;
32470 Roo.each(this.barItems, function(v){
32472 if (!v.isActive()) {
32484 setActiveItem : function(item)
32488 Roo.each(this.barItems, function(v){
32489 if (v.rid == item.rid) {
32493 if (v.isActive()) {
32494 v.setActive(false);
32499 item.setActive(true);
32501 this.fireEvent('changed', this, item, prev);
32504 getBarItem: function(rid)
32508 Roo.each(this.barItems, function(e) {
32509 if (e.rid != rid) {
32520 indexOfItem : function(item)
32524 Roo.each(this.barItems, function(v, i){
32526 if (v.rid != item.rid) {
32537 setActiveNext : function()
32539 var i = this.indexOfItem(this.getActive());
32541 if (i > this.barItems.length) {
32545 this.setActiveItem(this.barItems[i+1]);
32548 setActivePrev : function()
32550 var i = this.indexOfItem(this.getActive());
32556 this.setActiveItem(this.barItems[i-1]);
32559 format : function()
32561 if(!this.barItems.length){
32565 var width = 100 / this.barItems.length;
32567 Roo.each(this.barItems, function(i){
32568 i.el.setStyle('width', width + '%');
32569 i.topEl.el.setStyle('width', width + '%');
32570 i.bottomEl.el.setStyle('width', width + '%');
32579 * Nav Progress Item
32584 * @class Roo.bootstrap.NavProgressItem
32585 * @extends Roo.bootstrap.Component
32586 * Bootstrap NavProgressItem class
32587 * @cfg {String} rid the reference id
32588 * @cfg {Boolean} active (true|false) Is item active default false
32589 * @cfg {Boolean} disabled (true|false) Is item active default false
32590 * @cfg {String} html
32591 * @cfg {String} position (top|bottom) text position default bottom
32592 * @cfg {String} icon show icon instead of number
32595 * Create a new NavProgressItem
32596 * @param {Object} config The config object
32598 Roo.bootstrap.NavProgressItem = function(config){
32599 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32604 * The raw click event for the entire grid.
32605 * @param {Roo.bootstrap.NavProgressItem} this
32606 * @param {Roo.EventObject} e
32613 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32619 position : 'bottom',
32622 getAutoCreate : function()
32624 var iconCls = 'roo-navigation-bar-item-icon';
32626 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32630 cls: 'roo-navigation-bar-item',
32640 cfg.cls += ' active';
32643 cfg.cls += ' disabled';
32649 disable : function()
32651 this.setDisabled(true);
32654 enable : function()
32656 this.setDisabled(false);
32659 initEvents: function()
32661 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32663 this.iconEl.on('click', this.onClick, this);
32666 onClick : function(e)
32668 e.preventDefault();
32674 if(this.fireEvent('click', this, e) === false){
32678 this.parent().setActiveItem(this);
32681 isActive: function ()
32683 return this.active;
32686 setActive : function(state)
32688 if(this.active == state){
32692 this.active = state;
32695 this.el.addClass('active');
32699 this.el.removeClass('active');
32704 setDisabled : function(state)
32706 if(this.disabled == state){
32710 this.disabled = state;
32713 this.el.addClass('disabled');
32717 this.el.removeClass('disabled');
32720 tooltipEl : function()
32722 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32735 * @class Roo.bootstrap.FieldLabel
32736 * @extends Roo.bootstrap.Component
32737 * Bootstrap FieldLabel class
32738 * @cfg {String} html contents of the element
32739 * @cfg {String} tag tag of the element default label
32740 * @cfg {String} cls class of the element
32741 * @cfg {String} target label target
32742 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32743 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32744 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32745 * @cfg {String} iconTooltip default "This field is required"
32746 * @cfg {String} indicatorpos (left|right) default left
32749 * Create a new FieldLabel
32750 * @param {Object} config The config object
32753 Roo.bootstrap.FieldLabel = function(config){
32754 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32759 * Fires after the field has been marked as invalid.
32760 * @param {Roo.form.FieldLabel} this
32761 * @param {String} msg The validation message
32766 * Fires after the field has been validated with no errors.
32767 * @param {Roo.form.FieldLabel} this
32773 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32780 invalidClass : 'has-warning',
32781 validClass : 'has-success',
32782 iconTooltip : 'This field is required',
32783 indicatorpos : 'left',
32785 getAutoCreate : function(){
32788 if (!this.allowBlank) {
32794 cls : 'roo-bootstrap-field-label ' + this.cls,
32799 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32800 tooltip : this.iconTooltip
32809 if(this.indicatorpos == 'right'){
32812 cls : 'roo-bootstrap-field-label ' + this.cls,
32821 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32822 tooltip : this.iconTooltip
32831 initEvents: function()
32833 Roo.bootstrap.Element.superclass.initEvents.call(this);
32835 this.indicator = this.indicatorEl();
32837 if(this.indicator){
32838 this.indicator.removeClass('visible');
32839 this.indicator.addClass('invisible');
32842 Roo.bootstrap.FieldLabel.register(this);
32845 indicatorEl : function()
32847 var indicator = this.el.select('i.roo-required-indicator',true).first();
32858 * Mark this field as valid
32860 markValid : function()
32862 if(this.indicator){
32863 this.indicator.removeClass('visible');
32864 this.indicator.addClass('invisible');
32866 if (Roo.bootstrap.version == 3) {
32867 this.el.removeClass(this.invalidClass);
32868 this.el.addClass(this.validClass);
32870 this.el.removeClass('is-invalid');
32871 this.el.addClass('is-valid');
32875 this.fireEvent('valid', this);
32879 * Mark this field as invalid
32880 * @param {String} msg The validation message
32882 markInvalid : function(msg)
32884 if(this.indicator){
32885 this.indicator.removeClass('invisible');
32886 this.indicator.addClass('visible');
32888 if (Roo.bootstrap.version == 3) {
32889 this.el.removeClass(this.validClass);
32890 this.el.addClass(this.invalidClass);
32892 this.el.removeClass('is-valid');
32893 this.el.addClass('is-invalid');
32897 this.fireEvent('invalid', this, msg);
32903 Roo.apply(Roo.bootstrap.FieldLabel, {
32908 * register a FieldLabel Group
32909 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32911 register : function(label)
32913 if(this.groups.hasOwnProperty(label.target)){
32917 this.groups[label.target] = label;
32921 * fetch a FieldLabel Group based on the target
32922 * @param {string} target
32923 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32925 get: function(target) {
32926 if (typeof(this.groups[target]) == 'undefined') {
32930 return this.groups[target] ;
32939 * page DateSplitField.
32945 * @class Roo.bootstrap.DateSplitField
32946 * @extends Roo.bootstrap.Component
32947 * Bootstrap DateSplitField class
32948 * @cfg {string} fieldLabel - the label associated
32949 * @cfg {Number} labelWidth set the width of label (0-12)
32950 * @cfg {String} labelAlign (top|left)
32951 * @cfg {Boolean} dayAllowBlank (true|false) default false
32952 * @cfg {Boolean} monthAllowBlank (true|false) default false
32953 * @cfg {Boolean} yearAllowBlank (true|false) default false
32954 * @cfg {string} dayPlaceholder
32955 * @cfg {string} monthPlaceholder
32956 * @cfg {string} yearPlaceholder
32957 * @cfg {string} dayFormat default 'd'
32958 * @cfg {string} monthFormat default 'm'
32959 * @cfg {string} yearFormat default 'Y'
32960 * @cfg {Number} labellg set the width of label (1-12)
32961 * @cfg {Number} labelmd set the width of label (1-12)
32962 * @cfg {Number} labelsm set the width of label (1-12)
32963 * @cfg {Number} labelxs set the width of label (1-12)
32967 * Create a new DateSplitField
32968 * @param {Object} config The config object
32971 Roo.bootstrap.DateSplitField = function(config){
32972 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32978 * getting the data of years
32979 * @param {Roo.bootstrap.DateSplitField} this
32980 * @param {Object} years
32985 * getting the data of days
32986 * @param {Roo.bootstrap.DateSplitField} this
32987 * @param {Object} days
32992 * Fires after the field has been marked as invalid.
32993 * @param {Roo.form.Field} this
32994 * @param {String} msg The validation message
32999 * Fires after the field has been validated with no errors.
33000 * @param {Roo.form.Field} this
33006 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33009 labelAlign : 'top',
33011 dayAllowBlank : false,
33012 monthAllowBlank : false,
33013 yearAllowBlank : false,
33014 dayPlaceholder : '',
33015 monthPlaceholder : '',
33016 yearPlaceholder : '',
33020 isFormField : true,
33026 getAutoCreate : function()
33030 cls : 'row roo-date-split-field-group',
33035 cls : 'form-hidden-field roo-date-split-field-group-value',
33041 var labelCls = 'col-md-12';
33042 var contentCls = 'col-md-4';
33044 if(this.fieldLabel){
33048 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33052 html : this.fieldLabel
33057 if(this.labelAlign == 'left'){
33059 if(this.labelWidth > 12){
33060 label.style = "width: " + this.labelWidth + 'px';
33063 if(this.labelWidth < 13 && this.labelmd == 0){
33064 this.labelmd = this.labelWidth;
33067 if(this.labellg > 0){
33068 labelCls = ' col-lg-' + this.labellg;
33069 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33072 if(this.labelmd > 0){
33073 labelCls = ' col-md-' + this.labelmd;
33074 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33077 if(this.labelsm > 0){
33078 labelCls = ' col-sm-' + this.labelsm;
33079 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33082 if(this.labelxs > 0){
33083 labelCls = ' col-xs-' + this.labelxs;
33084 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33088 label.cls += ' ' + labelCls;
33090 cfg.cn.push(label);
33093 Roo.each(['day', 'month', 'year'], function(t){
33096 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33103 inputEl: function ()
33105 return this.el.select('.roo-date-split-field-group-value', true).first();
33108 onRender : function(ct, position)
33112 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33114 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33116 this.dayField = new Roo.bootstrap.ComboBox({
33117 allowBlank : this.dayAllowBlank,
33118 alwaysQuery : true,
33119 displayField : 'value',
33122 forceSelection : true,
33124 placeholder : this.dayPlaceholder,
33125 selectOnFocus : true,
33126 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33127 triggerAction : 'all',
33129 valueField : 'value',
33130 store : new Roo.data.SimpleStore({
33131 data : (function() {
33133 _this.fireEvent('days', _this, days);
33136 fields : [ 'value' ]
33139 select : function (_self, record, index)
33141 _this.setValue(_this.getValue());
33146 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33148 this.monthField = new Roo.bootstrap.MonthField({
33149 after : '<i class=\"fa fa-calendar\"></i>',
33150 allowBlank : this.monthAllowBlank,
33151 placeholder : this.monthPlaceholder,
33154 render : function (_self)
33156 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33157 e.preventDefault();
33161 select : function (_self, oldvalue, newvalue)
33163 _this.setValue(_this.getValue());
33168 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33170 this.yearField = new Roo.bootstrap.ComboBox({
33171 allowBlank : this.yearAllowBlank,
33172 alwaysQuery : true,
33173 displayField : 'value',
33176 forceSelection : true,
33178 placeholder : this.yearPlaceholder,
33179 selectOnFocus : true,
33180 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33181 triggerAction : 'all',
33183 valueField : 'value',
33184 store : new Roo.data.SimpleStore({
33185 data : (function() {
33187 _this.fireEvent('years', _this, years);
33190 fields : [ 'value' ]
33193 select : function (_self, record, index)
33195 _this.setValue(_this.getValue());
33200 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33203 setValue : function(v, format)
33205 this.inputEl.dom.value = v;
33207 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33209 var d = Date.parseDate(v, f);
33216 this.setDay(d.format(this.dayFormat));
33217 this.setMonth(d.format(this.monthFormat));
33218 this.setYear(d.format(this.yearFormat));
33225 setDay : function(v)
33227 this.dayField.setValue(v);
33228 this.inputEl.dom.value = this.getValue();
33233 setMonth : function(v)
33235 this.monthField.setValue(v, true);
33236 this.inputEl.dom.value = this.getValue();
33241 setYear : function(v)
33243 this.yearField.setValue(v);
33244 this.inputEl.dom.value = this.getValue();
33249 getDay : function()
33251 return this.dayField.getValue();
33254 getMonth : function()
33256 return this.monthField.getValue();
33259 getYear : function()
33261 return this.yearField.getValue();
33264 getValue : function()
33266 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33268 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33278 this.inputEl.dom.value = '';
33283 validate : function()
33285 var d = this.dayField.validate();
33286 var m = this.monthField.validate();
33287 var y = this.yearField.validate();
33292 (!this.dayAllowBlank && !d) ||
33293 (!this.monthAllowBlank && !m) ||
33294 (!this.yearAllowBlank && !y)
33299 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33308 this.markInvalid();
33313 markValid : function()
33316 var label = this.el.select('label', true).first();
33317 var icon = this.el.select('i.fa-star', true).first();
33323 this.fireEvent('valid', this);
33327 * Mark this field as invalid
33328 * @param {String} msg The validation message
33330 markInvalid : function(msg)
33333 var label = this.el.select('label', true).first();
33334 var icon = this.el.select('i.fa-star', true).first();
33336 if(label && !icon){
33337 this.el.select('.roo-date-split-field-label', true).createChild({
33339 cls : 'text-danger fa fa-lg fa-star',
33340 tooltip : 'This field is required',
33341 style : 'margin-right:5px;'
33345 this.fireEvent('invalid', this, msg);
33348 clearInvalid : function()
33350 var label = this.el.select('label', true).first();
33351 var icon = this.el.select('i.fa-star', true).first();
33357 this.fireEvent('valid', this);
33360 getName: function()
33370 * http://masonry.desandro.com
33372 * The idea is to render all the bricks based on vertical width...
33374 * The original code extends 'outlayer' - we might need to use that....
33380 * @class Roo.bootstrap.LayoutMasonry
33381 * @extends Roo.bootstrap.Component
33382 * Bootstrap Layout Masonry class
33385 * Create a new Element
33386 * @param {Object} config The config object
33389 Roo.bootstrap.LayoutMasonry = function(config){
33391 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33395 Roo.bootstrap.LayoutMasonry.register(this);
33401 * Fire after layout the items
33402 * @param {Roo.bootstrap.LayoutMasonry} this
33403 * @param {Roo.EventObject} e
33410 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33413 * @cfg {Boolean} isLayoutInstant = no animation?
33415 isLayoutInstant : false, // needed?
33418 * @cfg {Number} boxWidth width of the columns
33423 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33428 * @cfg {Number} padWidth padding below box..
33433 * @cfg {Number} gutter gutter width..
33438 * @cfg {Number} maxCols maximum number of columns
33444 * @cfg {Boolean} isAutoInitial defalut true
33446 isAutoInitial : true,
33451 * @cfg {Boolean} isHorizontal defalut false
33453 isHorizontal : false,
33455 currentSize : null,
33461 bricks: null, //CompositeElement
33465 _isLayoutInited : false,
33467 // isAlternative : false, // only use for vertical layout...
33470 * @cfg {Number} alternativePadWidth padding below box..
33472 alternativePadWidth : 50,
33474 selectedBrick : [],
33476 getAutoCreate : function(){
33478 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33482 cls: 'blog-masonary-wrapper ' + this.cls,
33484 cls : 'mas-boxes masonary'
33491 getChildContainer: function( )
33493 if (this.boxesEl) {
33494 return this.boxesEl;
33497 this.boxesEl = this.el.select('.mas-boxes').first();
33499 return this.boxesEl;
33503 initEvents : function()
33507 if(this.isAutoInitial){
33508 Roo.log('hook children rendered');
33509 this.on('childrenrendered', function() {
33510 Roo.log('children rendered');
33516 initial : function()
33518 this.selectedBrick = [];
33520 this.currentSize = this.el.getBox(true);
33522 Roo.EventManager.onWindowResize(this.resize, this);
33524 if(!this.isAutoInitial){
33532 //this.layout.defer(500,this);
33536 resize : function()
33538 var cs = this.el.getBox(true);
33541 this.currentSize.width == cs.width &&
33542 this.currentSize.x == cs.x &&
33543 this.currentSize.height == cs.height &&
33544 this.currentSize.y == cs.y
33546 Roo.log("no change in with or X or Y");
33550 this.currentSize = cs;
33556 layout : function()
33558 this._resetLayout();
33560 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33562 this.layoutItems( isInstant );
33564 this._isLayoutInited = true;
33566 this.fireEvent('layout', this);
33570 _resetLayout : function()
33572 if(this.isHorizontal){
33573 this.horizontalMeasureColumns();
33577 this.verticalMeasureColumns();
33581 verticalMeasureColumns : function()
33583 this.getContainerWidth();
33585 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33586 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33590 var boxWidth = this.boxWidth + this.padWidth;
33592 if(this.containerWidth < this.boxWidth){
33593 boxWidth = this.containerWidth
33596 var containerWidth = this.containerWidth;
33598 var cols = Math.floor(containerWidth / boxWidth);
33600 this.cols = Math.max( cols, 1 );
33602 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33604 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33606 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33608 this.colWidth = boxWidth + avail - this.padWidth;
33610 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33611 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33614 horizontalMeasureColumns : function()
33616 this.getContainerWidth();
33618 var boxWidth = this.boxWidth;
33620 if(this.containerWidth < boxWidth){
33621 boxWidth = this.containerWidth;
33624 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33626 this.el.setHeight(boxWidth);
33630 getContainerWidth : function()
33632 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33635 layoutItems : function( isInstant )
33637 Roo.log(this.bricks);
33639 var items = Roo.apply([], this.bricks);
33641 if(this.isHorizontal){
33642 this._horizontalLayoutItems( items , isInstant );
33646 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33647 // this._verticalAlternativeLayoutItems( items , isInstant );
33651 this._verticalLayoutItems( items , isInstant );
33655 _verticalLayoutItems : function ( items , isInstant)
33657 if ( !items || !items.length ) {
33662 ['xs', 'xs', 'xs', 'tall'],
33663 ['xs', 'xs', 'tall'],
33664 ['xs', 'xs', 'sm'],
33665 ['xs', 'xs', 'xs'],
33671 ['sm', 'xs', 'xs'],
33675 ['tall', 'xs', 'xs', 'xs'],
33676 ['tall', 'xs', 'xs'],
33688 Roo.each(items, function(item, k){
33690 switch (item.size) {
33691 // these layouts take up a full box,
33702 boxes.push([item]);
33725 var filterPattern = function(box, length)
33733 var pattern = box.slice(0, length);
33737 Roo.each(pattern, function(i){
33738 format.push(i.size);
33741 Roo.each(standard, function(s){
33743 if(String(s) != String(format)){
33752 if(!match && length == 1){
33757 filterPattern(box, length - 1);
33761 queue.push(pattern);
33763 box = box.slice(length, box.length);
33765 filterPattern(box, 4);
33771 Roo.each(boxes, function(box, k){
33777 if(box.length == 1){
33782 filterPattern(box, 4);
33786 this._processVerticalLayoutQueue( queue, isInstant );
33790 // _verticalAlternativeLayoutItems : function( items , isInstant )
33792 // if ( !items || !items.length ) {
33796 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33800 _horizontalLayoutItems : function ( items , isInstant)
33802 if ( !items || !items.length || items.length < 3) {
33808 var eItems = items.slice(0, 3);
33810 items = items.slice(3, items.length);
33813 ['xs', 'xs', 'xs', 'wide'],
33814 ['xs', 'xs', 'wide'],
33815 ['xs', 'xs', 'sm'],
33816 ['xs', 'xs', 'xs'],
33822 ['sm', 'xs', 'xs'],
33826 ['wide', 'xs', 'xs', 'xs'],
33827 ['wide', 'xs', 'xs'],
33840 Roo.each(items, function(item, k){
33842 switch (item.size) {
33853 boxes.push([item]);
33877 var filterPattern = function(box, length)
33885 var pattern = box.slice(0, length);
33889 Roo.each(pattern, function(i){
33890 format.push(i.size);
33893 Roo.each(standard, function(s){
33895 if(String(s) != String(format)){
33904 if(!match && length == 1){
33909 filterPattern(box, length - 1);
33913 queue.push(pattern);
33915 box = box.slice(length, box.length);
33917 filterPattern(box, 4);
33923 Roo.each(boxes, function(box, k){
33929 if(box.length == 1){
33934 filterPattern(box, 4);
33941 var pos = this.el.getBox(true);
33945 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33947 var hit_end = false;
33949 Roo.each(queue, function(box){
33953 Roo.each(box, function(b){
33955 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33965 Roo.each(box, function(b){
33967 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33970 mx = Math.max(mx, b.x);
33974 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33978 Roo.each(box, function(b){
33980 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33994 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33997 /** Sets position of item in DOM
33998 * @param {Element} item
33999 * @param {Number} x - horizontal position
34000 * @param {Number} y - vertical position
34001 * @param {Boolean} isInstant - disables transitions
34003 _processVerticalLayoutQueue : function( queue, isInstant )
34005 var pos = this.el.getBox(true);
34010 for (var i = 0; i < this.cols; i++){
34014 Roo.each(queue, function(box, k){
34016 var col = k % this.cols;
34018 Roo.each(box, function(b,kk){
34020 b.el.position('absolute');
34022 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34023 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34025 if(b.size == 'md-left' || b.size == 'md-right'){
34026 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34027 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34030 b.el.setWidth(width);
34031 b.el.setHeight(height);
34033 b.el.select('iframe',true).setSize(width,height);
34037 for (var i = 0; i < this.cols; i++){
34039 if(maxY[i] < maxY[col]){
34044 col = Math.min(col, i);
34048 x = pos.x + col * (this.colWidth + this.padWidth);
34052 var positions = [];
34054 switch (box.length){
34056 positions = this.getVerticalOneBoxColPositions(x, y, box);
34059 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34062 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34065 positions = this.getVerticalFourBoxColPositions(x, y, box);
34071 Roo.each(box, function(b,kk){
34073 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34075 var sz = b.el.getSize();
34077 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34085 for (var i = 0; i < this.cols; i++){
34086 mY = Math.max(mY, maxY[i]);
34089 this.el.setHeight(mY - pos.y);
34093 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34095 // var pos = this.el.getBox(true);
34098 // var maxX = pos.right;
34100 // var maxHeight = 0;
34102 // Roo.each(items, function(item, k){
34106 // item.el.position('absolute');
34108 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34110 // item.el.setWidth(width);
34112 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34114 // item.el.setHeight(height);
34117 // item.el.setXY([x, y], isInstant ? false : true);
34119 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34122 // y = y + height + this.alternativePadWidth;
34124 // maxHeight = maxHeight + height + this.alternativePadWidth;
34128 // this.el.setHeight(maxHeight);
34132 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34134 var pos = this.el.getBox(true);
34139 var maxX = pos.right;
34141 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34143 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34145 Roo.each(queue, function(box, k){
34147 Roo.each(box, function(b, kk){
34149 b.el.position('absolute');
34151 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34152 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34154 if(b.size == 'md-left' || b.size == 'md-right'){
34155 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34156 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34159 b.el.setWidth(width);
34160 b.el.setHeight(height);
34168 var positions = [];
34170 switch (box.length){
34172 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34175 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34178 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34181 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34187 Roo.each(box, function(b,kk){
34189 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34191 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34199 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34201 Roo.each(eItems, function(b,k){
34203 b.size = (k == 0) ? 'sm' : 'xs';
34204 b.x = (k == 0) ? 2 : 1;
34205 b.y = (k == 0) ? 2 : 1;
34207 b.el.position('absolute');
34209 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34211 b.el.setWidth(width);
34213 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34215 b.el.setHeight(height);
34219 var positions = [];
34222 x : maxX - this.unitWidth * 2 - this.gutter,
34227 x : maxX - this.unitWidth,
34228 y : minY + (this.unitWidth + this.gutter) * 2
34232 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34236 Roo.each(eItems, function(b,k){
34238 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34244 getVerticalOneBoxColPositions : function(x, y, box)
34248 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34250 if(box[0].size == 'md-left'){
34254 if(box[0].size == 'md-right'){
34259 x : x + (this.unitWidth + this.gutter) * rand,
34266 getVerticalTwoBoxColPositions : function(x, y, box)
34270 if(box[0].size == 'xs'){
34274 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34278 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34292 x : x + (this.unitWidth + this.gutter) * 2,
34293 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34300 getVerticalThreeBoxColPositions : function(x, y, box)
34304 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34312 x : x + (this.unitWidth + this.gutter) * 1,
34317 x : x + (this.unitWidth + this.gutter) * 2,
34325 if(box[0].size == 'xs' && box[1].size == 'xs'){
34334 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34338 x : x + (this.unitWidth + this.gutter) * 1,
34352 x : x + (this.unitWidth + this.gutter) * 2,
34357 x : x + (this.unitWidth + this.gutter) * 2,
34358 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34365 getVerticalFourBoxColPositions : function(x, y, box)
34369 if(box[0].size == 'xs'){
34378 y : y + (this.unitHeight + this.gutter) * 1
34383 y : y + (this.unitHeight + this.gutter) * 2
34387 x : x + (this.unitWidth + this.gutter) * 1,
34401 x : x + (this.unitWidth + this.gutter) * 2,
34406 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34407 y : y + (this.unitHeight + this.gutter) * 1
34411 x : x + (this.unitWidth + this.gutter) * 2,
34412 y : y + (this.unitWidth + this.gutter) * 2
34419 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34423 if(box[0].size == 'md-left'){
34425 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34432 if(box[0].size == 'md-right'){
34434 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34435 y : minY + (this.unitWidth + this.gutter) * 1
34441 var rand = Math.floor(Math.random() * (4 - box[0].y));
34444 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34445 y : minY + (this.unitWidth + this.gutter) * rand
34452 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34456 if(box[0].size == 'xs'){
34459 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34464 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34465 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34473 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34478 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34479 y : minY + (this.unitWidth + this.gutter) * 2
34486 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34490 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34493 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34498 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34499 y : minY + (this.unitWidth + this.gutter) * 1
34503 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34504 y : minY + (this.unitWidth + this.gutter) * 2
34511 if(box[0].size == 'xs' && box[1].size == 'xs'){
34514 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34519 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34524 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34525 y : minY + (this.unitWidth + this.gutter) * 1
34533 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34538 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34539 y : minY + (this.unitWidth + this.gutter) * 2
34543 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34544 y : minY + (this.unitWidth + this.gutter) * 2
34551 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34555 if(box[0].size == 'xs'){
34558 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34563 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34568 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),
34573 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34574 y : minY + (this.unitWidth + this.gutter) * 1
34582 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34587 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34588 y : minY + (this.unitWidth + this.gutter) * 2
34592 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34593 y : minY + (this.unitWidth + this.gutter) * 2
34597 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),
34598 y : minY + (this.unitWidth + this.gutter) * 2
34606 * remove a Masonry Brick
34607 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34609 removeBrick : function(brick_id)
34615 for (var i = 0; i<this.bricks.length; i++) {
34616 if (this.bricks[i].id == brick_id) {
34617 this.bricks.splice(i,1);
34618 this.el.dom.removeChild(Roo.get(brick_id).dom);
34625 * adds a Masonry Brick
34626 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34628 addBrick : function(cfg)
34630 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34631 //this.register(cn);
34632 cn.parentId = this.id;
34633 cn.render(this.el);
34638 * register a Masonry Brick
34639 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34642 register : function(brick)
34644 this.bricks.push(brick);
34645 brick.masonryId = this.id;
34649 * clear all the Masonry Brick
34651 clearAll : function()
34654 //this.getChildContainer().dom.innerHTML = "";
34655 this.el.dom.innerHTML = '';
34658 getSelected : function()
34660 if (!this.selectedBrick) {
34664 return this.selectedBrick;
34668 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34672 * register a Masonry Layout
34673 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34676 register : function(layout)
34678 this.groups[layout.id] = layout;
34681 * fetch a Masonry Layout based on the masonry layout ID
34682 * @param {string} the masonry layout to add
34683 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34686 get: function(layout_id) {
34687 if (typeof(this.groups[layout_id]) == 'undefined') {
34690 return this.groups[layout_id] ;
34702 * http://masonry.desandro.com
34704 * The idea is to render all the bricks based on vertical width...
34706 * The original code extends 'outlayer' - we might need to use that....
34712 * @class Roo.bootstrap.LayoutMasonryAuto
34713 * @extends Roo.bootstrap.Component
34714 * Bootstrap Layout Masonry class
34717 * Create a new Element
34718 * @param {Object} config The config object
34721 Roo.bootstrap.LayoutMasonryAuto = function(config){
34722 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34725 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34728 * @cfg {Boolean} isFitWidth - resize the width..
34730 isFitWidth : false, // options..
34732 * @cfg {Boolean} isOriginLeft = left align?
34734 isOriginLeft : true,
34736 * @cfg {Boolean} isOriginTop = top align?
34738 isOriginTop : false,
34740 * @cfg {Boolean} isLayoutInstant = no animation?
34742 isLayoutInstant : false, // needed?
34744 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34746 isResizingContainer : true,
34748 * @cfg {Number} columnWidth width of the columns
34754 * @cfg {Number} maxCols maximum number of columns
34759 * @cfg {Number} padHeight padding below box..
34765 * @cfg {Boolean} isAutoInitial defalut true
34768 isAutoInitial : true,
34774 initialColumnWidth : 0,
34775 currentSize : null,
34777 colYs : null, // array.
34784 bricks: null, //CompositeElement
34785 cols : 0, // array?
34786 // element : null, // wrapped now this.el
34787 _isLayoutInited : null,
34790 getAutoCreate : function(){
34794 cls: 'blog-masonary-wrapper ' + this.cls,
34796 cls : 'mas-boxes masonary'
34803 getChildContainer: function( )
34805 if (this.boxesEl) {
34806 return this.boxesEl;
34809 this.boxesEl = this.el.select('.mas-boxes').first();
34811 return this.boxesEl;
34815 initEvents : function()
34819 if(this.isAutoInitial){
34820 Roo.log('hook children rendered');
34821 this.on('childrenrendered', function() {
34822 Roo.log('children rendered');
34829 initial : function()
34831 this.reloadItems();
34833 this.currentSize = this.el.getBox(true);
34835 /// was window resize... - let's see if this works..
34836 Roo.EventManager.onWindowResize(this.resize, this);
34838 if(!this.isAutoInitial){
34843 this.layout.defer(500,this);
34846 reloadItems: function()
34848 this.bricks = this.el.select('.masonry-brick', true);
34850 this.bricks.each(function(b) {
34851 //Roo.log(b.getSize());
34852 if (!b.attr('originalwidth')) {
34853 b.attr('originalwidth', b.getSize().width);
34858 Roo.log(this.bricks.elements.length);
34861 resize : function()
34864 var cs = this.el.getBox(true);
34866 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34867 Roo.log("no change in with or X");
34870 this.currentSize = cs;
34874 layout : function()
34877 this._resetLayout();
34878 //this._manageStamps();
34880 // don't animate first layout
34881 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34882 this.layoutItems( isInstant );
34884 // flag for initalized
34885 this._isLayoutInited = true;
34888 layoutItems : function( isInstant )
34890 //var items = this._getItemsForLayout( this.items );
34891 // original code supports filtering layout items.. we just ignore it..
34893 this._layoutItems( this.bricks , isInstant );
34895 this._postLayout();
34897 _layoutItems : function ( items , isInstant)
34899 //this.fireEvent( 'layout', this, items );
34902 if ( !items || !items.elements.length ) {
34903 // no items, emit event with empty array
34908 items.each(function(item) {
34909 Roo.log("layout item");
34911 // get x/y object from method
34912 var position = this._getItemLayoutPosition( item );
34914 position.item = item;
34915 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34916 queue.push( position );
34919 this._processLayoutQueue( queue );
34921 /** Sets position of item in DOM
34922 * @param {Element} item
34923 * @param {Number} x - horizontal position
34924 * @param {Number} y - vertical position
34925 * @param {Boolean} isInstant - disables transitions
34927 _processLayoutQueue : function( queue )
34929 for ( var i=0, len = queue.length; i < len; i++ ) {
34930 var obj = queue[i];
34931 obj.item.position('absolute');
34932 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34938 * Any logic you want to do after each layout,
34939 * i.e. size the container
34941 _postLayout : function()
34943 this.resizeContainer();
34946 resizeContainer : function()
34948 if ( !this.isResizingContainer ) {
34951 var size = this._getContainerSize();
34953 this.el.setSize(size.width,size.height);
34954 this.boxesEl.setSize(size.width,size.height);
34960 _resetLayout : function()
34962 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34963 this.colWidth = this.el.getWidth();
34964 //this.gutter = this.el.getWidth();
34966 this.measureColumns();
34972 this.colYs.push( 0 );
34978 measureColumns : function()
34980 this.getContainerWidth();
34981 // if columnWidth is 0, default to outerWidth of first item
34982 if ( !this.columnWidth ) {
34983 var firstItem = this.bricks.first();
34984 Roo.log(firstItem);
34985 this.columnWidth = this.containerWidth;
34986 if (firstItem && firstItem.attr('originalwidth') ) {
34987 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34989 // columnWidth fall back to item of first element
34990 Roo.log("set column width?");
34991 this.initialColumnWidth = this.columnWidth ;
34993 // if first elem has no width, default to size of container
34998 if (this.initialColumnWidth) {
34999 this.columnWidth = this.initialColumnWidth;
35004 // column width is fixed at the top - however if container width get's smaller we should
35007 // this bit calcs how man columns..
35009 var columnWidth = this.columnWidth += this.gutter;
35011 // calculate columns
35012 var containerWidth = this.containerWidth + this.gutter;
35014 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35015 // fix rounding errors, typically with gutters
35016 var excess = columnWidth - containerWidth % columnWidth;
35019 // if overshoot is less than a pixel, round up, otherwise floor it
35020 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35021 cols = Math[ mathMethod ]( cols );
35022 this.cols = Math.max( cols, 1 );
35023 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35025 // padding positioning..
35026 var totalColWidth = this.cols * this.columnWidth;
35027 var padavail = this.containerWidth - totalColWidth;
35028 // so for 2 columns - we need 3 'pads'
35030 var padNeeded = (1+this.cols) * this.padWidth;
35032 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35034 this.columnWidth += padExtra
35035 //this.padWidth = Math.floor(padavail / ( this.cols));
35037 // adjust colum width so that padding is fixed??
35039 // we have 3 columns ... total = width * 3
35040 // we have X left over... that should be used by
35042 //if (this.expandC) {
35050 getContainerWidth : function()
35052 /* // container is parent if fit width
35053 var container = this.isFitWidth ? this.element.parentNode : this.element;
35054 // check that this.size and size are there
35055 // IE8 triggers resize on body size change, so they might not be
35057 var size = getSize( container ); //FIXME
35058 this.containerWidth = size && size.innerWidth; //FIXME
35061 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35065 _getItemLayoutPosition : function( item ) // what is item?
35067 // we resize the item to our columnWidth..
35069 item.setWidth(this.columnWidth);
35070 item.autoBoxAdjust = false;
35072 var sz = item.getSize();
35074 // how many columns does this brick span
35075 var remainder = this.containerWidth % this.columnWidth;
35077 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35078 // round if off by 1 pixel, otherwise use ceil
35079 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35080 colSpan = Math.min( colSpan, this.cols );
35082 // normally this should be '1' as we dont' currently allow multi width columns..
35084 var colGroup = this._getColGroup( colSpan );
35085 // get the minimum Y value from the columns
35086 var minimumY = Math.min.apply( Math, colGroup );
35087 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35089 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35091 // position the brick
35093 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35094 y: this.currentSize.y + minimumY + this.padHeight
35098 // apply setHeight to necessary columns
35099 var setHeight = minimumY + sz.height + this.padHeight;
35100 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35102 var setSpan = this.cols + 1 - colGroup.length;
35103 for ( var i = 0; i < setSpan; i++ ) {
35104 this.colYs[ shortColIndex + i ] = setHeight ;
35111 * @param {Number} colSpan - number of columns the element spans
35112 * @returns {Array} colGroup
35114 _getColGroup : function( colSpan )
35116 if ( colSpan < 2 ) {
35117 // if brick spans only one column, use all the column Ys
35122 // how many different places could this brick fit horizontally
35123 var groupCount = this.cols + 1 - colSpan;
35124 // for each group potential horizontal position
35125 for ( var i = 0; i < groupCount; i++ ) {
35126 // make an array of colY values for that one group
35127 var groupColYs = this.colYs.slice( i, i + colSpan );
35128 // and get the max value of the array
35129 colGroup[i] = Math.max.apply( Math, groupColYs );
35134 _manageStamp : function( stamp )
35136 var stampSize = stamp.getSize();
35137 var offset = stamp.getBox();
35138 // get the columns that this stamp affects
35139 var firstX = this.isOriginLeft ? offset.x : offset.right;
35140 var lastX = firstX + stampSize.width;
35141 var firstCol = Math.floor( firstX / this.columnWidth );
35142 firstCol = Math.max( 0, firstCol );
35144 var lastCol = Math.floor( lastX / this.columnWidth );
35145 // lastCol should not go over if multiple of columnWidth #425
35146 lastCol -= lastX % this.columnWidth ? 0 : 1;
35147 lastCol = Math.min( this.cols - 1, lastCol );
35149 // set colYs to bottom of the stamp
35150 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35153 for ( var i = firstCol; i <= lastCol; i++ ) {
35154 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35159 _getContainerSize : function()
35161 this.maxY = Math.max.apply( Math, this.colYs );
35166 if ( this.isFitWidth ) {
35167 size.width = this._getContainerFitWidth();
35173 _getContainerFitWidth : function()
35175 var unusedCols = 0;
35176 // count unused columns
35179 if ( this.colYs[i] !== 0 ) {
35184 // fit container to columns that have been used
35185 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35188 needsResizeLayout : function()
35190 var previousWidth = this.containerWidth;
35191 this.getContainerWidth();
35192 return previousWidth !== this.containerWidth;
35207 * @class Roo.bootstrap.MasonryBrick
35208 * @extends Roo.bootstrap.Component
35209 * Bootstrap MasonryBrick class
35212 * Create a new MasonryBrick
35213 * @param {Object} config The config object
35216 Roo.bootstrap.MasonryBrick = function(config){
35218 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35220 Roo.bootstrap.MasonryBrick.register(this);
35226 * When a MasonryBrick is clcik
35227 * @param {Roo.bootstrap.MasonryBrick} this
35228 * @param {Roo.EventObject} e
35234 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35237 * @cfg {String} title
35241 * @cfg {String} html
35245 * @cfg {String} bgimage
35249 * @cfg {String} videourl
35253 * @cfg {String} cls
35257 * @cfg {String} href
35261 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35266 * @cfg {String} placetitle (center|bottom)
35271 * @cfg {Boolean} isFitContainer defalut true
35273 isFitContainer : true,
35276 * @cfg {Boolean} preventDefault defalut false
35278 preventDefault : false,
35281 * @cfg {Boolean} inverse defalut false
35283 maskInverse : false,
35285 getAutoCreate : function()
35287 if(!this.isFitContainer){
35288 return this.getSplitAutoCreate();
35291 var cls = 'masonry-brick masonry-brick-full';
35293 if(this.href.length){
35294 cls += ' masonry-brick-link';
35297 if(this.bgimage.length){
35298 cls += ' masonry-brick-image';
35301 if(this.maskInverse){
35302 cls += ' mask-inverse';
35305 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35306 cls += ' enable-mask';
35310 cls += ' masonry-' + this.size + '-brick';
35313 if(this.placetitle.length){
35315 switch (this.placetitle) {
35317 cls += ' masonry-center-title';
35320 cls += ' masonry-bottom-title';
35327 if(!this.html.length && !this.bgimage.length){
35328 cls += ' masonry-center-title';
35331 if(!this.html.length && this.bgimage.length){
35332 cls += ' masonry-bottom-title';
35337 cls += ' ' + this.cls;
35341 tag: (this.href.length) ? 'a' : 'div',
35346 cls: 'masonry-brick-mask'
35350 cls: 'masonry-brick-paragraph',
35356 if(this.href.length){
35357 cfg.href = this.href;
35360 var cn = cfg.cn[1].cn;
35362 if(this.title.length){
35365 cls: 'masonry-brick-title',
35370 if(this.html.length){
35373 cls: 'masonry-brick-text',
35378 if (!this.title.length && !this.html.length) {
35379 cfg.cn[1].cls += ' hide';
35382 if(this.bgimage.length){
35385 cls: 'masonry-brick-image-view',
35390 if(this.videourl.length){
35391 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35392 // youtube support only?
35395 cls: 'masonry-brick-image-view',
35398 allowfullscreen : true
35406 getSplitAutoCreate : function()
35408 var cls = 'masonry-brick masonry-brick-split';
35410 if(this.href.length){
35411 cls += ' masonry-brick-link';
35414 if(this.bgimage.length){
35415 cls += ' masonry-brick-image';
35419 cls += ' masonry-' + this.size + '-brick';
35422 switch (this.placetitle) {
35424 cls += ' masonry-center-title';
35427 cls += ' masonry-bottom-title';
35430 if(!this.bgimage.length){
35431 cls += ' masonry-center-title';
35434 if(this.bgimage.length){
35435 cls += ' masonry-bottom-title';
35441 cls += ' ' + this.cls;
35445 tag: (this.href.length) ? 'a' : 'div',
35450 cls: 'masonry-brick-split-head',
35454 cls: 'masonry-brick-paragraph',
35461 cls: 'masonry-brick-split-body',
35467 if(this.href.length){
35468 cfg.href = this.href;
35471 if(this.title.length){
35472 cfg.cn[0].cn[0].cn.push({
35474 cls: 'masonry-brick-title',
35479 if(this.html.length){
35480 cfg.cn[1].cn.push({
35482 cls: 'masonry-brick-text',
35487 if(this.bgimage.length){
35488 cfg.cn[0].cn.push({
35490 cls: 'masonry-brick-image-view',
35495 if(this.videourl.length){
35496 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35497 // youtube support only?
35498 cfg.cn[0].cn.cn.push({
35500 cls: 'masonry-brick-image-view',
35503 allowfullscreen : true
35510 initEvents: function()
35512 switch (this.size) {
35545 this.el.on('touchstart', this.onTouchStart, this);
35546 this.el.on('touchmove', this.onTouchMove, this);
35547 this.el.on('touchend', this.onTouchEnd, this);
35548 this.el.on('contextmenu', this.onContextMenu, this);
35550 this.el.on('mouseenter' ,this.enter, this);
35551 this.el.on('mouseleave', this.leave, this);
35552 this.el.on('click', this.onClick, this);
35555 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35556 this.parent().bricks.push(this);
35561 onClick: function(e, el)
35563 var time = this.endTimer - this.startTimer;
35564 // Roo.log(e.preventDefault());
35567 e.preventDefault();
35572 if(!this.preventDefault){
35576 e.preventDefault();
35578 if (this.activeClass != '') {
35579 this.selectBrick();
35582 this.fireEvent('click', this, e);
35585 enter: function(e, el)
35587 e.preventDefault();
35589 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35593 if(this.bgimage.length && this.html.length){
35594 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35598 leave: function(e, el)
35600 e.preventDefault();
35602 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35606 if(this.bgimage.length && this.html.length){
35607 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35611 onTouchStart: function(e, el)
35613 // e.preventDefault();
35615 this.touchmoved = false;
35617 if(!this.isFitContainer){
35621 if(!this.bgimage.length || !this.html.length){
35625 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35627 this.timer = new Date().getTime();
35631 onTouchMove: function(e, el)
35633 this.touchmoved = true;
35636 onContextMenu : function(e,el)
35638 e.preventDefault();
35639 e.stopPropagation();
35643 onTouchEnd: function(e, el)
35645 // e.preventDefault();
35647 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35654 if(!this.bgimage.length || !this.html.length){
35656 if(this.href.length){
35657 window.location.href = this.href;
35663 if(!this.isFitContainer){
35667 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35669 window.location.href = this.href;
35672 //selection on single brick only
35673 selectBrick : function() {
35675 if (!this.parentId) {
35679 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35680 var index = m.selectedBrick.indexOf(this.id);
35683 m.selectedBrick.splice(index,1);
35684 this.el.removeClass(this.activeClass);
35688 for(var i = 0; i < m.selectedBrick.length; i++) {
35689 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35690 b.el.removeClass(b.activeClass);
35693 m.selectedBrick = [];
35695 m.selectedBrick.push(this.id);
35696 this.el.addClass(this.activeClass);
35700 isSelected : function(){
35701 return this.el.hasClass(this.activeClass);
35706 Roo.apply(Roo.bootstrap.MasonryBrick, {
35709 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35711 * register a Masonry Brick
35712 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35715 register : function(brick)
35717 //this.groups[brick.id] = brick;
35718 this.groups.add(brick.id, brick);
35721 * fetch a masonry brick based on the masonry brick ID
35722 * @param {string} the masonry brick to add
35723 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35726 get: function(brick_id)
35728 // if (typeof(this.groups[brick_id]) == 'undefined') {
35731 // return this.groups[brick_id] ;
35733 if(this.groups.key(brick_id)) {
35734 return this.groups.key(brick_id);
35752 * @class Roo.bootstrap.Brick
35753 * @extends Roo.bootstrap.Component
35754 * Bootstrap Brick class
35757 * Create a new Brick
35758 * @param {Object} config The config object
35761 Roo.bootstrap.Brick = function(config){
35762 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35768 * When a Brick is click
35769 * @param {Roo.bootstrap.Brick} this
35770 * @param {Roo.EventObject} e
35776 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35779 * @cfg {String} title
35783 * @cfg {String} html
35787 * @cfg {String} bgimage
35791 * @cfg {String} cls
35795 * @cfg {String} href
35799 * @cfg {String} video
35803 * @cfg {Boolean} square
35807 getAutoCreate : function()
35809 var cls = 'roo-brick';
35811 if(this.href.length){
35812 cls += ' roo-brick-link';
35815 if(this.bgimage.length){
35816 cls += ' roo-brick-image';
35819 if(!this.html.length && !this.bgimage.length){
35820 cls += ' roo-brick-center-title';
35823 if(!this.html.length && this.bgimage.length){
35824 cls += ' roo-brick-bottom-title';
35828 cls += ' ' + this.cls;
35832 tag: (this.href.length) ? 'a' : 'div',
35837 cls: 'roo-brick-paragraph',
35843 if(this.href.length){
35844 cfg.href = this.href;
35847 var cn = cfg.cn[0].cn;
35849 if(this.title.length){
35852 cls: 'roo-brick-title',
35857 if(this.html.length){
35860 cls: 'roo-brick-text',
35867 if(this.bgimage.length){
35870 cls: 'roo-brick-image-view',
35878 initEvents: function()
35880 if(this.title.length || this.html.length){
35881 this.el.on('mouseenter' ,this.enter, this);
35882 this.el.on('mouseleave', this.leave, this);
35885 Roo.EventManager.onWindowResize(this.resize, this);
35887 if(this.bgimage.length){
35888 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35889 this.imageEl.on('load', this.onImageLoad, this);
35896 onImageLoad : function()
35901 resize : function()
35903 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35905 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35907 if(this.bgimage.length){
35908 var image = this.el.select('.roo-brick-image-view', true).first();
35910 image.setWidth(paragraph.getWidth());
35913 image.setHeight(paragraph.getWidth());
35916 this.el.setHeight(image.getHeight());
35917 paragraph.setHeight(image.getHeight());
35923 enter: function(e, el)
35925 e.preventDefault();
35927 if(this.bgimage.length){
35928 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35929 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35933 leave: function(e, el)
35935 e.preventDefault();
35937 if(this.bgimage.length){
35938 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35939 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35954 * @class Roo.bootstrap.NumberField
35955 * @extends Roo.bootstrap.Input
35956 * Bootstrap NumberField class
35962 * Create a new NumberField
35963 * @param {Object} config The config object
35966 Roo.bootstrap.NumberField = function(config){
35967 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35970 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35973 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35975 allowDecimals : true,
35977 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35979 decimalSeparator : ".",
35981 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35983 decimalPrecision : 2,
35985 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35987 allowNegative : true,
35990 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35994 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35996 minValue : Number.NEGATIVE_INFINITY,
35998 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36000 maxValue : Number.MAX_VALUE,
36002 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36004 minText : "The minimum value for this field is {0}",
36006 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36008 maxText : "The maximum value for this field is {0}",
36010 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36011 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36013 nanText : "{0} is not a valid number",
36015 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36017 thousandsDelimiter : false,
36019 * @cfg {String} valueAlign alignment of value
36021 valueAlign : "left",
36023 getAutoCreate : function()
36025 var hiddenInput = {
36029 cls: 'hidden-number-input'
36033 hiddenInput.name = this.name;
36038 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36040 this.name = hiddenInput.name;
36042 if(cfg.cn.length > 0) {
36043 cfg.cn.push(hiddenInput);
36050 initEvents : function()
36052 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36054 var allowed = "0123456789";
36056 if(this.allowDecimals){
36057 allowed += this.decimalSeparator;
36060 if(this.allowNegative){
36064 if(this.thousandsDelimiter) {
36068 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36070 var keyPress = function(e){
36072 var k = e.getKey();
36074 var c = e.getCharCode();
36077 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36078 allowed.indexOf(String.fromCharCode(c)) === -1
36084 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36088 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36093 this.el.on("keypress", keyPress, this);
36096 validateValue : function(value)
36099 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36103 var num = this.parseValue(value);
36106 this.markInvalid(String.format(this.nanText, value));
36110 if(num < this.minValue){
36111 this.markInvalid(String.format(this.minText, this.minValue));
36115 if(num > this.maxValue){
36116 this.markInvalid(String.format(this.maxText, this.maxValue));
36123 getValue : function()
36125 var v = this.hiddenEl().getValue();
36127 return this.fixPrecision(this.parseValue(v));
36130 parseValue : function(value)
36132 if(this.thousandsDelimiter) {
36134 r = new RegExp(",", "g");
36135 value = value.replace(r, "");
36138 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36139 return isNaN(value) ? '' : value;
36142 fixPrecision : function(value)
36144 if(this.thousandsDelimiter) {
36146 r = new RegExp(",", "g");
36147 value = value.replace(r, "");
36150 var nan = isNaN(value);
36152 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36153 return nan ? '' : value;
36155 return parseFloat(value).toFixed(this.decimalPrecision);
36158 setValue : function(v)
36160 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36166 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36168 this.inputEl().dom.value = (v == '') ? '' :
36169 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36171 if(!this.allowZero && v === '0') {
36172 this.hiddenEl().dom.value = '';
36173 this.inputEl().dom.value = '';
36180 decimalPrecisionFcn : function(v)
36182 return Math.floor(v);
36185 beforeBlur : function()
36187 var v = this.parseValue(this.getRawValue());
36189 if(v || v === 0 || v === ''){
36194 hiddenEl : function()
36196 return this.el.select('input.hidden-number-input',true).first();
36208 * @class Roo.bootstrap.DocumentSlider
36209 * @extends Roo.bootstrap.Component
36210 * Bootstrap DocumentSlider class
36213 * Create a new DocumentViewer
36214 * @param {Object} config The config object
36217 Roo.bootstrap.DocumentSlider = function(config){
36218 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36225 * Fire after initEvent
36226 * @param {Roo.bootstrap.DocumentSlider} this
36231 * Fire after update
36232 * @param {Roo.bootstrap.DocumentSlider} this
36238 * @param {Roo.bootstrap.DocumentSlider} this
36244 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36250 getAutoCreate : function()
36254 cls : 'roo-document-slider',
36258 cls : 'roo-document-slider-header',
36262 cls : 'roo-document-slider-header-title'
36268 cls : 'roo-document-slider-body',
36272 cls : 'roo-document-slider-prev',
36276 cls : 'fa fa-chevron-left'
36282 cls : 'roo-document-slider-thumb',
36286 cls : 'roo-document-slider-image'
36292 cls : 'roo-document-slider-next',
36296 cls : 'fa fa-chevron-right'
36308 initEvents : function()
36310 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36311 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36313 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36314 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36316 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36317 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36319 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36320 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36322 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36323 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36325 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36326 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36328 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36329 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36331 this.thumbEl.on('click', this.onClick, this);
36333 this.prevIndicator.on('click', this.prev, this);
36335 this.nextIndicator.on('click', this.next, this);
36339 initial : function()
36341 if(this.files.length){
36342 this.indicator = 1;
36346 this.fireEvent('initial', this);
36349 update : function()
36351 this.imageEl.attr('src', this.files[this.indicator - 1]);
36353 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36355 this.prevIndicator.show();
36357 if(this.indicator == 1){
36358 this.prevIndicator.hide();
36361 this.nextIndicator.show();
36363 if(this.indicator == this.files.length){
36364 this.nextIndicator.hide();
36367 this.thumbEl.scrollTo('top');
36369 this.fireEvent('update', this);
36372 onClick : function(e)
36374 e.preventDefault();
36376 this.fireEvent('click', this);
36381 e.preventDefault();
36383 this.indicator = Math.max(1, this.indicator - 1);
36390 e.preventDefault();
36392 this.indicator = Math.min(this.files.length, this.indicator + 1);
36406 * @class Roo.bootstrap.RadioSet
36407 * @extends Roo.bootstrap.Input
36408 * Bootstrap RadioSet class
36409 * @cfg {String} indicatorpos (left|right) default left
36410 * @cfg {Boolean} inline (true|false) inline the element (default true)
36411 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36413 * Create a new RadioSet
36414 * @param {Object} config The config object
36417 Roo.bootstrap.RadioSet = function(config){
36419 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36423 Roo.bootstrap.RadioSet.register(this);
36428 * Fires when the element is checked or unchecked.
36429 * @param {Roo.bootstrap.RadioSet} this This radio
36430 * @param {Roo.bootstrap.Radio} item The checked item
36435 * Fires when the element is click.
36436 * @param {Roo.bootstrap.RadioSet} this This radio set
36437 * @param {Roo.bootstrap.Radio} item The checked item
36438 * @param {Roo.EventObject} e The event object
36445 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36453 indicatorpos : 'left',
36455 getAutoCreate : function()
36459 cls : 'roo-radio-set-label',
36463 html : this.fieldLabel
36467 if (Roo.bootstrap.version == 3) {
36470 if(this.indicatorpos == 'left'){
36473 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36474 tooltip : 'This field is required'
36479 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36480 tooltip : 'This field is required'
36486 cls : 'roo-radio-set-items'
36489 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36491 if (align === 'left' && this.fieldLabel.length) {
36494 cls : "roo-radio-set-right",
36500 if(this.labelWidth > 12){
36501 label.style = "width: " + this.labelWidth + 'px';
36504 if(this.labelWidth < 13 && this.labelmd == 0){
36505 this.labelmd = this.labelWidth;
36508 if(this.labellg > 0){
36509 label.cls += ' col-lg-' + this.labellg;
36510 items.cls += ' col-lg-' + (12 - this.labellg);
36513 if(this.labelmd > 0){
36514 label.cls += ' col-md-' + this.labelmd;
36515 items.cls += ' col-md-' + (12 - this.labelmd);
36518 if(this.labelsm > 0){
36519 label.cls += ' col-sm-' + this.labelsm;
36520 items.cls += ' col-sm-' + (12 - this.labelsm);
36523 if(this.labelxs > 0){
36524 label.cls += ' col-xs-' + this.labelxs;
36525 items.cls += ' col-xs-' + (12 - this.labelxs);
36531 cls : 'roo-radio-set',
36535 cls : 'roo-radio-set-input',
36538 value : this.value ? this.value : ''
36545 if(this.weight.length){
36546 cfg.cls += ' roo-radio-' + this.weight;
36550 cfg.cls += ' roo-radio-set-inline';
36554 ['xs','sm','md','lg'].map(function(size){
36555 if (settings[size]) {
36556 cfg.cls += ' col-' + size + '-' + settings[size];
36564 initEvents : function()
36566 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36567 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36569 if(!this.fieldLabel.length){
36570 this.labelEl.hide();
36573 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36574 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36576 this.indicator = this.indicatorEl();
36578 if(this.indicator){
36579 this.indicator.addClass('invisible');
36582 this.originalValue = this.getValue();
36586 inputEl: function ()
36588 return this.el.select('.roo-radio-set-input', true).first();
36591 getChildContainer : function()
36593 return this.itemsEl;
36596 register : function(item)
36598 this.radioes.push(item);
36602 validate : function()
36604 if(this.getVisibilityEl().hasClass('hidden')){
36610 Roo.each(this.radioes, function(i){
36619 if(this.allowBlank) {
36623 if(this.disabled || valid){
36628 this.markInvalid();
36633 markValid : function()
36635 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36636 this.indicatorEl().removeClass('visible');
36637 this.indicatorEl().addClass('invisible');
36641 if (Roo.bootstrap.version == 3) {
36642 this.el.removeClass([this.invalidClass, this.validClass]);
36643 this.el.addClass(this.validClass);
36645 this.el.removeClass(['is-invalid','is-valid']);
36646 this.el.addClass(['is-valid']);
36648 this.fireEvent('valid', this);
36651 markInvalid : function(msg)
36653 if(this.allowBlank || this.disabled){
36657 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36658 this.indicatorEl().removeClass('invisible');
36659 this.indicatorEl().addClass('visible');
36661 if (Roo.bootstrap.version == 3) {
36662 this.el.removeClass([this.invalidClass, this.validClass]);
36663 this.el.addClass(this.invalidClass);
36665 this.el.removeClass(['is-invalid','is-valid']);
36666 this.el.addClass(['is-invalid']);
36669 this.fireEvent('invalid', this, msg);
36673 setValue : function(v, suppressEvent)
36675 if(this.value === v){
36682 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36685 Roo.each(this.radioes, function(i){
36687 i.el.removeClass('checked');
36690 Roo.each(this.radioes, function(i){
36692 if(i.value === v || i.value.toString() === v.toString()){
36694 i.el.addClass('checked');
36696 if(suppressEvent !== true){
36697 this.fireEvent('check', this, i);
36708 clearInvalid : function(){
36710 if(!this.el || this.preventMark){
36714 this.el.removeClass([this.invalidClass]);
36716 this.fireEvent('valid', this);
36721 Roo.apply(Roo.bootstrap.RadioSet, {
36725 register : function(set)
36727 this.groups[set.name] = set;
36730 get: function(name)
36732 if (typeof(this.groups[name]) == 'undefined') {
36736 return this.groups[name] ;
36742 * Ext JS Library 1.1.1
36743 * Copyright(c) 2006-2007, Ext JS, LLC.
36745 * Originally Released Under LGPL - original licence link has changed is not relivant.
36748 * <script type="text/javascript">
36753 * @class Roo.bootstrap.SplitBar
36754 * @extends Roo.util.Observable
36755 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36759 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36760 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36761 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36762 split.minSize = 100;
36763 split.maxSize = 600;
36764 split.animate = true;
36765 split.on('moved', splitterMoved);
36768 * Create a new SplitBar
36769 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36770 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36771 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36772 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36773 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36774 position of the SplitBar).
36776 Roo.bootstrap.SplitBar = function(cfg){
36781 // dragElement : elm
36782 // resizingElement: el,
36784 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36785 // placement : Roo.bootstrap.SplitBar.LEFT ,
36786 // existingProxy ???
36789 this.el = Roo.get(cfg.dragElement, true);
36790 this.el.dom.unselectable = "on";
36792 this.resizingEl = Roo.get(cfg.resizingElement, true);
36796 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36797 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36800 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36803 * The minimum size of the resizing element. (Defaults to 0)
36809 * The maximum size of the resizing element. (Defaults to 2000)
36812 this.maxSize = 2000;
36815 * Whether to animate the transition to the new size
36818 this.animate = false;
36821 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36824 this.useShim = false;
36829 if(!cfg.existingProxy){
36831 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36833 this.proxy = Roo.get(cfg.existingProxy).dom;
36836 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36839 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36842 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36845 this.dragSpecs = {};
36848 * @private The adapter to use to positon and resize elements
36850 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36851 this.adapter.init(this);
36853 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36855 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36856 this.el.addClass("roo-splitbar-h");
36859 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36860 this.el.addClass("roo-splitbar-v");
36866 * Fires when the splitter is moved (alias for {@link #event-moved})
36867 * @param {Roo.bootstrap.SplitBar} this
36868 * @param {Number} newSize the new width or height
36873 * Fires when the splitter is moved
36874 * @param {Roo.bootstrap.SplitBar} this
36875 * @param {Number} newSize the new width or height
36879 * @event beforeresize
36880 * Fires before the splitter is dragged
36881 * @param {Roo.bootstrap.SplitBar} this
36883 "beforeresize" : true,
36885 "beforeapply" : true
36888 Roo.util.Observable.call(this);
36891 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36892 onStartProxyDrag : function(x, y){
36893 this.fireEvent("beforeresize", this);
36895 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36897 o.enableDisplayMode("block");
36898 // all splitbars share the same overlay
36899 Roo.bootstrap.SplitBar.prototype.overlay = o;
36901 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36902 this.overlay.show();
36903 Roo.get(this.proxy).setDisplayed("block");
36904 var size = this.adapter.getElementSize(this);
36905 this.activeMinSize = this.getMinimumSize();;
36906 this.activeMaxSize = this.getMaximumSize();;
36907 var c1 = size - this.activeMinSize;
36908 var c2 = Math.max(this.activeMaxSize - size, 0);
36909 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36910 this.dd.resetConstraints();
36911 this.dd.setXConstraint(
36912 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36913 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36915 this.dd.setYConstraint(0, 0);
36917 this.dd.resetConstraints();
36918 this.dd.setXConstraint(0, 0);
36919 this.dd.setYConstraint(
36920 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36921 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36924 this.dragSpecs.startSize = size;
36925 this.dragSpecs.startPoint = [x, y];
36926 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36930 * @private Called after the drag operation by the DDProxy
36932 onEndProxyDrag : function(e){
36933 Roo.get(this.proxy).setDisplayed(false);
36934 var endPoint = Roo.lib.Event.getXY(e);
36936 this.overlay.hide();
36939 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36940 newSize = this.dragSpecs.startSize +
36941 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36942 endPoint[0] - this.dragSpecs.startPoint[0] :
36943 this.dragSpecs.startPoint[0] - endPoint[0]
36946 newSize = this.dragSpecs.startSize +
36947 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36948 endPoint[1] - this.dragSpecs.startPoint[1] :
36949 this.dragSpecs.startPoint[1] - endPoint[1]
36952 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36953 if(newSize != this.dragSpecs.startSize){
36954 if(this.fireEvent('beforeapply', this, newSize) !== false){
36955 this.adapter.setElementSize(this, newSize);
36956 this.fireEvent("moved", this, newSize);
36957 this.fireEvent("resize", this, newSize);
36963 * Get the adapter this SplitBar uses
36964 * @return The adapter object
36966 getAdapter : function(){
36967 return this.adapter;
36971 * Set the adapter this SplitBar uses
36972 * @param {Object} adapter A SplitBar adapter object
36974 setAdapter : function(adapter){
36975 this.adapter = adapter;
36976 this.adapter.init(this);
36980 * Gets the minimum size for the resizing element
36981 * @return {Number} The minimum size
36983 getMinimumSize : function(){
36984 return this.minSize;
36988 * Sets the minimum size for the resizing element
36989 * @param {Number} minSize The minimum size
36991 setMinimumSize : function(minSize){
36992 this.minSize = minSize;
36996 * Gets the maximum size for the resizing element
36997 * @return {Number} The maximum size
36999 getMaximumSize : function(){
37000 return this.maxSize;
37004 * Sets the maximum size for the resizing element
37005 * @param {Number} maxSize The maximum size
37007 setMaximumSize : function(maxSize){
37008 this.maxSize = maxSize;
37012 * Sets the initialize size for the resizing element
37013 * @param {Number} size The initial size
37015 setCurrentSize : function(size){
37016 var oldAnimate = this.animate;
37017 this.animate = false;
37018 this.adapter.setElementSize(this, size);
37019 this.animate = oldAnimate;
37023 * Destroy this splitbar.
37024 * @param {Boolean} removeEl True to remove the element
37026 destroy : function(removeEl){
37028 this.shim.remove();
37031 this.proxy.parentNode.removeChild(this.proxy);
37039 * @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.
37041 Roo.bootstrap.SplitBar.createProxy = function(dir){
37042 var proxy = new Roo.Element(document.createElement("div"));
37043 proxy.unselectable();
37044 var cls = 'roo-splitbar-proxy';
37045 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37046 document.body.appendChild(proxy.dom);
37051 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37052 * Default Adapter. It assumes the splitter and resizing element are not positioned
37053 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37055 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37058 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37059 // do nothing for now
37060 init : function(s){
37064 * Called before drag operations to get the current size of the resizing element.
37065 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37067 getElementSize : function(s){
37068 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37069 return s.resizingEl.getWidth();
37071 return s.resizingEl.getHeight();
37076 * Called after drag operations to set the size of the resizing element.
37077 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37078 * @param {Number} newSize The new size to set
37079 * @param {Function} onComplete A function to be invoked when resizing is complete
37081 setElementSize : function(s, newSize, onComplete){
37082 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37084 s.resizingEl.setWidth(newSize);
37086 onComplete(s, newSize);
37089 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37094 s.resizingEl.setHeight(newSize);
37096 onComplete(s, newSize);
37099 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37106 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37107 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37108 * Adapter that moves the splitter element to align with the resized sizing element.
37109 * Used with an absolute positioned SplitBar.
37110 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37111 * document.body, make sure you assign an id to the body element.
37113 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37114 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37115 this.container = Roo.get(container);
37118 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37119 init : function(s){
37120 this.basic.init(s);
37123 getElementSize : function(s){
37124 return this.basic.getElementSize(s);
37127 setElementSize : function(s, newSize, onComplete){
37128 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37131 moveSplitter : function(s){
37132 var yes = Roo.bootstrap.SplitBar;
37133 switch(s.placement){
37135 s.el.setX(s.resizingEl.getRight());
37138 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37141 s.el.setY(s.resizingEl.getBottom());
37144 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37151 * Orientation constant - Create a vertical SplitBar
37155 Roo.bootstrap.SplitBar.VERTICAL = 1;
37158 * Orientation constant - Create a horizontal SplitBar
37162 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37165 * Placement constant - The resizing element is to the left of the splitter element
37169 Roo.bootstrap.SplitBar.LEFT = 1;
37172 * Placement constant - The resizing element is to the right of the splitter element
37176 Roo.bootstrap.SplitBar.RIGHT = 2;
37179 * Placement constant - The resizing element is positioned above the splitter element
37183 Roo.bootstrap.SplitBar.TOP = 3;
37186 * Placement constant - The resizing element is positioned under splitter element
37190 Roo.bootstrap.SplitBar.BOTTOM = 4;
37191 Roo.namespace("Roo.bootstrap.layout");/*
37193 * Ext JS Library 1.1.1
37194 * Copyright(c) 2006-2007, Ext JS, LLC.
37196 * Originally Released Under LGPL - original licence link has changed is not relivant.
37199 * <script type="text/javascript">
37203 * @class Roo.bootstrap.layout.Manager
37204 * @extends Roo.bootstrap.Component
37205 * Base class for layout managers.
37207 Roo.bootstrap.layout.Manager = function(config)
37209 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37215 /** false to disable window resize monitoring @type Boolean */
37216 this.monitorWindowResize = true;
37221 * Fires when a layout is performed.
37222 * @param {Roo.LayoutManager} this
37226 * @event regionresized
37227 * Fires when the user resizes a region.
37228 * @param {Roo.LayoutRegion} region The resized region
37229 * @param {Number} newSize The new size (width for east/west, height for north/south)
37231 "regionresized" : true,
37233 * @event regioncollapsed
37234 * Fires when a region is collapsed.
37235 * @param {Roo.LayoutRegion} region The collapsed region
37237 "regioncollapsed" : true,
37239 * @event regionexpanded
37240 * Fires when a region is expanded.
37241 * @param {Roo.LayoutRegion} region The expanded region
37243 "regionexpanded" : true
37245 this.updating = false;
37248 this.el = Roo.get(config.el);
37254 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37259 monitorWindowResize : true,
37265 onRender : function(ct, position)
37268 this.el = Roo.get(ct);
37271 //this.fireEvent('render',this);
37275 initEvents: function()
37279 // ie scrollbar fix
37280 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37281 document.body.scroll = "no";
37282 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37283 this.el.position('relative');
37285 this.id = this.el.id;
37286 this.el.addClass("roo-layout-container");
37287 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37288 if(this.el.dom != document.body ) {
37289 this.el.on('resize', this.layout,this);
37290 this.el.on('show', this.layout,this);
37296 * Returns true if this layout is currently being updated
37297 * @return {Boolean}
37299 isUpdating : function(){
37300 return this.updating;
37304 * Suspend the LayoutManager from doing auto-layouts while
37305 * making multiple add or remove calls
37307 beginUpdate : function(){
37308 this.updating = true;
37312 * Restore auto-layouts and optionally disable the manager from performing a layout
37313 * @param {Boolean} noLayout true to disable a layout update
37315 endUpdate : function(noLayout){
37316 this.updating = false;
37322 layout: function(){
37326 onRegionResized : function(region, newSize){
37327 this.fireEvent("regionresized", region, newSize);
37331 onRegionCollapsed : function(region){
37332 this.fireEvent("regioncollapsed", region);
37335 onRegionExpanded : function(region){
37336 this.fireEvent("regionexpanded", region);
37340 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37341 * performs box-model adjustments.
37342 * @return {Object} The size as an object {width: (the width), height: (the height)}
37344 getViewSize : function()
37347 if(this.el.dom != document.body){
37348 size = this.el.getSize();
37350 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37352 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37353 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37358 * Returns the Element this layout is bound to.
37359 * @return {Roo.Element}
37361 getEl : function(){
37366 * Returns the specified region.
37367 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37368 * @return {Roo.LayoutRegion}
37370 getRegion : function(target){
37371 return this.regions[target.toLowerCase()];
37374 onWindowResize : function(){
37375 if(this.monitorWindowResize){
37382 * Ext JS Library 1.1.1
37383 * Copyright(c) 2006-2007, Ext JS, LLC.
37385 * Originally Released Under LGPL - original licence link has changed is not relivant.
37388 * <script type="text/javascript">
37391 * @class Roo.bootstrap.layout.Border
37392 * @extends Roo.bootstrap.layout.Manager
37393 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37394 * please see: examples/bootstrap/nested.html<br><br>
37396 <b>The container the layout is rendered into can be either the body element or any other element.
37397 If it is not the body element, the container needs to either be an absolute positioned element,
37398 or you will need to add "position:relative" to the css of the container. You will also need to specify
37399 the container size if it is not the body element.</b>
37402 * Create a new Border
37403 * @param {Object} config Configuration options
37405 Roo.bootstrap.layout.Border = function(config){
37406 config = config || {};
37407 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37411 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37412 if(config[region]){
37413 config[region].region = region;
37414 this.addRegion(config[region]);
37420 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37422 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37424 parent : false, // this might point to a 'nest' or a ???
37427 * Creates and adds a new region if it doesn't already exist.
37428 * @param {String} target The target region key (north, south, east, west or center).
37429 * @param {Object} config The regions config object
37430 * @return {BorderLayoutRegion} The new region
37432 addRegion : function(config)
37434 if(!this.regions[config.region]){
37435 var r = this.factory(config);
37436 this.bindRegion(r);
37438 return this.regions[config.region];
37442 bindRegion : function(r){
37443 this.regions[r.config.region] = r;
37445 r.on("visibilitychange", this.layout, this);
37446 r.on("paneladded", this.layout, this);
37447 r.on("panelremoved", this.layout, this);
37448 r.on("invalidated", this.layout, this);
37449 r.on("resized", this.onRegionResized, this);
37450 r.on("collapsed", this.onRegionCollapsed, this);
37451 r.on("expanded", this.onRegionExpanded, this);
37455 * Performs a layout update.
37457 layout : function()
37459 if(this.updating) {
37463 // render all the rebions if they have not been done alreayd?
37464 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37465 if(this.regions[region] && !this.regions[region].bodyEl){
37466 this.regions[region].onRender(this.el)
37470 var size = this.getViewSize();
37471 var w = size.width;
37472 var h = size.height;
37477 //var x = 0, y = 0;
37479 var rs = this.regions;
37480 var north = rs["north"];
37481 var south = rs["south"];
37482 var west = rs["west"];
37483 var east = rs["east"];
37484 var center = rs["center"];
37485 //if(this.hideOnLayout){ // not supported anymore
37486 //c.el.setStyle("display", "none");
37488 if(north && north.isVisible()){
37489 var b = north.getBox();
37490 var m = north.getMargins();
37491 b.width = w - (m.left+m.right);
37494 centerY = b.height + b.y + m.bottom;
37495 centerH -= centerY;
37496 north.updateBox(this.safeBox(b));
37498 if(south && south.isVisible()){
37499 var b = south.getBox();
37500 var m = south.getMargins();
37501 b.width = w - (m.left+m.right);
37503 var totalHeight = (b.height + m.top + m.bottom);
37504 b.y = h - totalHeight + m.top;
37505 centerH -= totalHeight;
37506 south.updateBox(this.safeBox(b));
37508 if(west && west.isVisible()){
37509 var b = west.getBox();
37510 var m = west.getMargins();
37511 b.height = centerH - (m.top+m.bottom);
37513 b.y = centerY + m.top;
37514 var totalWidth = (b.width + m.left + m.right);
37515 centerX += totalWidth;
37516 centerW -= totalWidth;
37517 west.updateBox(this.safeBox(b));
37519 if(east && east.isVisible()){
37520 var b = east.getBox();
37521 var m = east.getMargins();
37522 b.height = centerH - (m.top+m.bottom);
37523 var totalWidth = (b.width + m.left + m.right);
37524 b.x = w - totalWidth + m.left;
37525 b.y = centerY + m.top;
37526 centerW -= totalWidth;
37527 east.updateBox(this.safeBox(b));
37530 var m = center.getMargins();
37532 x: centerX + m.left,
37533 y: centerY + m.top,
37534 width: centerW - (m.left+m.right),
37535 height: centerH - (m.top+m.bottom)
37537 //if(this.hideOnLayout){
37538 //center.el.setStyle("display", "block");
37540 center.updateBox(this.safeBox(centerBox));
37543 this.fireEvent("layout", this);
37547 safeBox : function(box){
37548 box.width = Math.max(0, box.width);
37549 box.height = Math.max(0, box.height);
37554 * Adds a ContentPanel (or subclass) to this layout.
37555 * @param {String} target The target region key (north, south, east, west or center).
37556 * @param {Roo.ContentPanel} panel The panel to add
37557 * @return {Roo.ContentPanel} The added panel
37559 add : function(target, panel){
37561 target = target.toLowerCase();
37562 return this.regions[target].add(panel);
37566 * Remove a ContentPanel (or subclass) to this layout.
37567 * @param {String} target The target region key (north, south, east, west or center).
37568 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37569 * @return {Roo.ContentPanel} The removed panel
37571 remove : function(target, panel){
37572 target = target.toLowerCase();
37573 return this.regions[target].remove(panel);
37577 * Searches all regions for a panel with the specified id
37578 * @param {String} panelId
37579 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37581 findPanel : function(panelId){
37582 var rs = this.regions;
37583 for(var target in rs){
37584 if(typeof rs[target] != "function"){
37585 var p = rs[target].getPanel(panelId);
37595 * Searches all regions for a panel with the specified id and activates (shows) it.
37596 * @param {String/ContentPanel} panelId The panels id or the panel itself
37597 * @return {Roo.ContentPanel} The shown panel or null
37599 showPanel : function(panelId) {
37600 var rs = this.regions;
37601 for(var target in rs){
37602 var r = rs[target];
37603 if(typeof r != "function"){
37604 if(r.hasPanel(panelId)){
37605 return r.showPanel(panelId);
37613 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37614 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37617 restoreState : function(provider){
37619 provider = Roo.state.Manager;
37621 var sm = new Roo.LayoutStateManager();
37622 sm.init(this, provider);
37628 * Adds a xtype elements to the layout.
37632 xtype : 'ContentPanel',
37639 xtype : 'NestedLayoutPanel',
37645 items : [ ... list of content panels or nested layout panels.. ]
37649 * @param {Object} cfg Xtype definition of item to add.
37651 addxtype : function(cfg)
37653 // basically accepts a pannel...
37654 // can accept a layout region..!?!?
37655 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37658 // theory? children can only be panels??
37660 //if (!cfg.xtype.match(/Panel$/)) {
37665 if (typeof(cfg.region) == 'undefined') {
37666 Roo.log("Failed to add Panel, region was not set");
37670 var region = cfg.region;
37676 xitems = cfg.items;
37681 if ( region == 'center') {
37682 Roo.log("Center: " + cfg.title);
37688 case 'Content': // ContentPanel (el, cfg)
37689 case 'Scroll': // ContentPanel (el, cfg)
37691 cfg.autoCreate = cfg.autoCreate || true;
37692 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37694 // var el = this.el.createChild();
37695 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37698 this.add(region, ret);
37702 case 'TreePanel': // our new panel!
37703 cfg.el = this.el.createChild();
37704 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37705 this.add(region, ret);
37710 // create a new Layout (which is a Border Layout...
37712 var clayout = cfg.layout;
37713 clayout.el = this.el.createChild();
37714 clayout.items = clayout.items || [];
37718 // replace this exitems with the clayout ones..
37719 xitems = clayout.items;
37721 // force background off if it's in center...
37722 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37723 cfg.background = false;
37725 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37728 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37729 //console.log('adding nested layout panel ' + cfg.toSource());
37730 this.add(region, ret);
37731 nb = {}; /// find first...
37736 // needs grid and region
37738 //var el = this.getRegion(region).el.createChild();
37740 *var el = this.el.createChild();
37741 // create the grid first...
37742 cfg.grid.container = el;
37743 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37746 if (region == 'center' && this.active ) {
37747 cfg.background = false;
37750 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37752 this.add(region, ret);
37754 if (cfg.background) {
37755 // render grid on panel activation (if panel background)
37756 ret.on('activate', function(gp) {
37757 if (!gp.grid.rendered) {
37758 // gp.grid.render(el);
37762 // cfg.grid.render(el);
37768 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37769 // it was the old xcomponent building that caused this before.
37770 // espeically if border is the top element in the tree.
37780 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37782 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37783 this.add(region, ret);
37787 throw "Can not add '" + cfg.xtype + "' to Border";
37793 this.beginUpdate();
37797 Roo.each(xitems, function(i) {
37798 region = nb && i.region ? i.region : false;
37800 var add = ret.addxtype(i);
37803 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37804 if (!i.background) {
37805 abn[region] = nb[region] ;
37812 // make the last non-background panel active..
37813 //if (nb) { Roo.log(abn); }
37816 for(var r in abn) {
37817 region = this.getRegion(r);
37819 // tried using nb[r], but it does not work..
37821 region.showPanel(abn[r]);
37832 factory : function(cfg)
37835 var validRegions = Roo.bootstrap.layout.Border.regions;
37837 var target = cfg.region;
37840 var r = Roo.bootstrap.layout;
37844 return new r.North(cfg);
37846 return new r.South(cfg);
37848 return new r.East(cfg);
37850 return new r.West(cfg);
37852 return new r.Center(cfg);
37854 throw 'Layout region "'+target+'" not supported.';
37861 * Ext JS Library 1.1.1
37862 * Copyright(c) 2006-2007, Ext JS, LLC.
37864 * Originally Released Under LGPL - original licence link has changed is not relivant.
37867 * <script type="text/javascript">
37871 * @class Roo.bootstrap.layout.Basic
37872 * @extends Roo.util.Observable
37873 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37874 * and does not have a titlebar, tabs or any other features. All it does is size and position
37875 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37876 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37877 * @cfg {string} region the region that it inhabits..
37878 * @cfg {bool} skipConfig skip config?
37882 Roo.bootstrap.layout.Basic = function(config){
37884 this.mgr = config.mgr;
37886 this.position = config.region;
37888 var skipConfig = config.skipConfig;
37892 * @scope Roo.BasicLayoutRegion
37896 * @event beforeremove
37897 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37898 * @param {Roo.LayoutRegion} this
37899 * @param {Roo.ContentPanel} panel The panel
37900 * @param {Object} e The cancel event object
37902 "beforeremove" : true,
37904 * @event invalidated
37905 * Fires when the layout for this region is changed.
37906 * @param {Roo.LayoutRegion} this
37908 "invalidated" : true,
37910 * @event visibilitychange
37911 * Fires when this region is shown or hidden
37912 * @param {Roo.LayoutRegion} this
37913 * @param {Boolean} visibility true or false
37915 "visibilitychange" : true,
37917 * @event paneladded
37918 * Fires when a panel is added.
37919 * @param {Roo.LayoutRegion} this
37920 * @param {Roo.ContentPanel} panel The panel
37922 "paneladded" : true,
37924 * @event panelremoved
37925 * Fires when a panel is removed.
37926 * @param {Roo.LayoutRegion} this
37927 * @param {Roo.ContentPanel} panel The panel
37929 "panelremoved" : true,
37931 * @event beforecollapse
37932 * Fires when this region before collapse.
37933 * @param {Roo.LayoutRegion} this
37935 "beforecollapse" : true,
37938 * Fires when this region is collapsed.
37939 * @param {Roo.LayoutRegion} this
37941 "collapsed" : true,
37944 * Fires when this region is expanded.
37945 * @param {Roo.LayoutRegion} this
37950 * Fires when this region is slid into view.
37951 * @param {Roo.LayoutRegion} this
37953 "slideshow" : true,
37956 * Fires when this region slides out of view.
37957 * @param {Roo.LayoutRegion} this
37959 "slidehide" : true,
37961 * @event panelactivated
37962 * Fires when a panel is activated.
37963 * @param {Roo.LayoutRegion} this
37964 * @param {Roo.ContentPanel} panel The activated panel
37966 "panelactivated" : true,
37969 * Fires when the user resizes this region.
37970 * @param {Roo.LayoutRegion} this
37971 * @param {Number} newSize The new size (width for east/west, height for north/south)
37975 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37976 this.panels = new Roo.util.MixedCollection();
37977 this.panels.getKey = this.getPanelId.createDelegate(this);
37979 this.activePanel = null;
37980 // ensure listeners are added...
37982 if (config.listeners || config.events) {
37983 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37984 listeners : config.listeners || {},
37985 events : config.events || {}
37989 if(skipConfig !== true){
37990 this.applyConfig(config);
37994 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37996 getPanelId : function(p){
38000 applyConfig : function(config){
38001 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38002 this.config = config;
38007 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38008 * the width, for horizontal (north, south) the height.
38009 * @param {Number} newSize The new width or height
38011 resizeTo : function(newSize){
38012 var el = this.el ? this.el :
38013 (this.activePanel ? this.activePanel.getEl() : null);
38015 switch(this.position){
38018 el.setWidth(newSize);
38019 this.fireEvent("resized", this, newSize);
38023 el.setHeight(newSize);
38024 this.fireEvent("resized", this, newSize);
38030 getBox : function(){
38031 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38034 getMargins : function(){
38035 return this.margins;
38038 updateBox : function(box){
38040 var el = this.activePanel.getEl();
38041 el.dom.style.left = box.x + "px";
38042 el.dom.style.top = box.y + "px";
38043 this.activePanel.setSize(box.width, box.height);
38047 * Returns the container element for this region.
38048 * @return {Roo.Element}
38050 getEl : function(){
38051 return this.activePanel;
38055 * Returns true if this region is currently visible.
38056 * @return {Boolean}
38058 isVisible : function(){
38059 return this.activePanel ? true : false;
38062 setActivePanel : function(panel){
38063 panel = this.getPanel(panel);
38064 if(this.activePanel && this.activePanel != panel){
38065 this.activePanel.setActiveState(false);
38066 this.activePanel.getEl().setLeftTop(-10000,-10000);
38068 this.activePanel = panel;
38069 panel.setActiveState(true);
38071 panel.setSize(this.box.width, this.box.height);
38073 this.fireEvent("panelactivated", this, panel);
38074 this.fireEvent("invalidated");
38078 * Show the specified panel.
38079 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38080 * @return {Roo.ContentPanel} The shown panel or null
38082 showPanel : function(panel){
38083 panel = this.getPanel(panel);
38085 this.setActivePanel(panel);
38091 * Get the active panel for this region.
38092 * @return {Roo.ContentPanel} The active panel or null
38094 getActivePanel : function(){
38095 return this.activePanel;
38099 * Add the passed ContentPanel(s)
38100 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38101 * @return {Roo.ContentPanel} The panel added (if only one was added)
38103 add : function(panel){
38104 if(arguments.length > 1){
38105 for(var i = 0, len = arguments.length; i < len; i++) {
38106 this.add(arguments[i]);
38110 if(this.hasPanel(panel)){
38111 this.showPanel(panel);
38114 var el = panel.getEl();
38115 if(el.dom.parentNode != this.mgr.el.dom){
38116 this.mgr.el.dom.appendChild(el.dom);
38118 if(panel.setRegion){
38119 panel.setRegion(this);
38121 this.panels.add(panel);
38122 el.setStyle("position", "absolute");
38123 if(!panel.background){
38124 this.setActivePanel(panel);
38125 if(this.config.initialSize && this.panels.getCount()==1){
38126 this.resizeTo(this.config.initialSize);
38129 this.fireEvent("paneladded", this, panel);
38134 * Returns true if the panel is in this region.
38135 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38136 * @return {Boolean}
38138 hasPanel : function(panel){
38139 if(typeof panel == "object"){ // must be panel obj
38140 panel = panel.getId();
38142 return this.getPanel(panel) ? true : false;
38146 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38147 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38148 * @param {Boolean} preservePanel Overrides the config preservePanel option
38149 * @return {Roo.ContentPanel} The panel that was removed
38151 remove : function(panel, preservePanel){
38152 panel = this.getPanel(panel);
38157 this.fireEvent("beforeremove", this, panel, e);
38158 if(e.cancel === true){
38161 var panelId = panel.getId();
38162 this.panels.removeKey(panelId);
38167 * Returns the panel specified or null if it's not in this region.
38168 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38169 * @return {Roo.ContentPanel}
38171 getPanel : function(id){
38172 if(typeof id == "object"){ // must be panel obj
38175 return this.panels.get(id);
38179 * Returns this regions position (north/south/east/west/center).
38182 getPosition: function(){
38183 return this.position;
38187 * Ext JS Library 1.1.1
38188 * Copyright(c) 2006-2007, Ext JS, LLC.
38190 * Originally Released Under LGPL - original licence link has changed is not relivant.
38193 * <script type="text/javascript">
38197 * @class Roo.bootstrap.layout.Region
38198 * @extends Roo.bootstrap.layout.Basic
38199 * This class represents a region in a layout manager.
38201 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38202 * @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})
38203 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38204 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38205 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38206 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38207 * @cfg {String} title The title for the region (overrides panel titles)
38208 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38209 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38210 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38211 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38212 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38213 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38214 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38215 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38216 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38217 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38219 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38220 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38221 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38222 * @cfg {Number} width For East/West panels
38223 * @cfg {Number} height For North/South panels
38224 * @cfg {Boolean} split To show the splitter
38225 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38227 * @cfg {string} cls Extra CSS classes to add to region
38229 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38230 * @cfg {string} region the region that it inhabits..
38233 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38234 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38236 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38237 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38238 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38240 Roo.bootstrap.layout.Region = function(config)
38242 this.applyConfig(config);
38244 var mgr = config.mgr;
38245 var pos = config.region;
38246 config.skipConfig = true;
38247 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38250 this.onRender(mgr.el);
38253 this.visible = true;
38254 this.collapsed = false;
38255 this.unrendered_panels = [];
38258 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38260 position: '', // set by wrapper (eg. north/south etc..)
38261 unrendered_panels : null, // unrendered panels.
38263 tabPosition : false,
38265 mgr: false, // points to 'Border'
38268 createBody : function(){
38269 /** This region's body element
38270 * @type Roo.Element */
38271 this.bodyEl = this.el.createChild({
38273 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38277 onRender: function(ctr, pos)
38279 var dh = Roo.DomHelper;
38280 /** This region's container element
38281 * @type Roo.Element */
38282 this.el = dh.append(ctr.dom, {
38284 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38286 /** This region's title element
38287 * @type Roo.Element */
38289 this.titleEl = dh.append(this.el.dom, {
38291 unselectable: "on",
38292 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38294 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38295 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38299 this.titleEl.enableDisplayMode();
38300 /** This region's title text element
38301 * @type HTMLElement */
38302 this.titleTextEl = this.titleEl.dom.firstChild;
38303 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38305 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38306 this.closeBtn.enableDisplayMode();
38307 this.closeBtn.on("click", this.closeClicked, this);
38308 this.closeBtn.hide();
38310 this.createBody(this.config);
38311 if(this.config.hideWhenEmpty){
38313 this.on("paneladded", this.validateVisibility, this);
38314 this.on("panelremoved", this.validateVisibility, this);
38316 if(this.autoScroll){
38317 this.bodyEl.setStyle("overflow", "auto");
38319 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38321 //if(c.titlebar !== false){
38322 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38323 this.titleEl.hide();
38325 this.titleEl.show();
38326 if(this.config.title){
38327 this.titleTextEl.innerHTML = this.config.title;
38331 if(this.config.collapsed){
38332 this.collapse(true);
38334 if(this.config.hidden){
38338 if (this.unrendered_panels && this.unrendered_panels.length) {
38339 for (var i =0;i< this.unrendered_panels.length; i++) {
38340 this.add(this.unrendered_panels[i]);
38342 this.unrendered_panels = null;
38348 applyConfig : function(c)
38351 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38352 var dh = Roo.DomHelper;
38353 if(c.titlebar !== false){
38354 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38355 this.collapseBtn.on("click", this.collapse, this);
38356 this.collapseBtn.enableDisplayMode();
38358 if(c.showPin === true || this.showPin){
38359 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38360 this.stickBtn.enableDisplayMode();
38361 this.stickBtn.on("click", this.expand, this);
38362 this.stickBtn.hide();
38367 /** This region's collapsed element
38368 * @type Roo.Element */
38371 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38372 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38375 if(c.floatable !== false){
38376 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38377 this.collapsedEl.on("click", this.collapseClick, this);
38380 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38381 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38382 id: "message", unselectable: "on", style:{"float":"left"}});
38383 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38385 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38386 this.expandBtn.on("click", this.expand, this);
38390 if(this.collapseBtn){
38391 this.collapseBtn.setVisible(c.collapsible == true);
38394 this.cmargins = c.cmargins || this.cmargins ||
38395 (this.position == "west" || this.position == "east" ?
38396 {top: 0, left: 2, right:2, bottom: 0} :
38397 {top: 2, left: 0, right:0, bottom: 2});
38399 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38402 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38404 this.autoScroll = c.autoScroll || false;
38409 this.duration = c.duration || .30;
38410 this.slideDuration = c.slideDuration || .45;
38415 * Returns true if this region is currently visible.
38416 * @return {Boolean}
38418 isVisible : function(){
38419 return this.visible;
38423 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38424 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38426 //setCollapsedTitle : function(title){
38427 // title = title || " ";
38428 // if(this.collapsedTitleTextEl){
38429 // this.collapsedTitleTextEl.innerHTML = title;
38433 getBox : function(){
38435 // if(!this.collapsed){
38436 b = this.el.getBox(false, true);
38438 // b = this.collapsedEl.getBox(false, true);
38443 getMargins : function(){
38444 return this.margins;
38445 //return this.collapsed ? this.cmargins : this.margins;
38448 highlight : function(){
38449 this.el.addClass("x-layout-panel-dragover");
38452 unhighlight : function(){
38453 this.el.removeClass("x-layout-panel-dragover");
38456 updateBox : function(box)
38458 if (!this.bodyEl) {
38459 return; // not rendered yet..
38463 if(!this.collapsed){
38464 this.el.dom.style.left = box.x + "px";
38465 this.el.dom.style.top = box.y + "px";
38466 this.updateBody(box.width, box.height);
38468 this.collapsedEl.dom.style.left = box.x + "px";
38469 this.collapsedEl.dom.style.top = box.y + "px";
38470 this.collapsedEl.setSize(box.width, box.height);
38473 this.tabs.autoSizeTabs();
38477 updateBody : function(w, h)
38480 this.el.setWidth(w);
38481 w -= this.el.getBorderWidth("rl");
38482 if(this.config.adjustments){
38483 w += this.config.adjustments[0];
38486 if(h !== null && h > 0){
38487 this.el.setHeight(h);
38488 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38489 h -= this.el.getBorderWidth("tb");
38490 if(this.config.adjustments){
38491 h += this.config.adjustments[1];
38493 this.bodyEl.setHeight(h);
38495 h = this.tabs.syncHeight(h);
38498 if(this.panelSize){
38499 w = w !== null ? w : this.panelSize.width;
38500 h = h !== null ? h : this.panelSize.height;
38502 if(this.activePanel){
38503 var el = this.activePanel.getEl();
38504 w = w !== null ? w : el.getWidth();
38505 h = h !== null ? h : el.getHeight();
38506 this.panelSize = {width: w, height: h};
38507 this.activePanel.setSize(w, h);
38509 if(Roo.isIE && this.tabs){
38510 this.tabs.el.repaint();
38515 * Returns the container element for this region.
38516 * @return {Roo.Element}
38518 getEl : function(){
38523 * Hides this region.
38526 //if(!this.collapsed){
38527 this.el.dom.style.left = "-2000px";
38530 // this.collapsedEl.dom.style.left = "-2000px";
38531 // this.collapsedEl.hide();
38533 this.visible = false;
38534 this.fireEvent("visibilitychange", this, false);
38538 * Shows this region if it was previously hidden.
38541 //if(!this.collapsed){
38544 // this.collapsedEl.show();
38546 this.visible = true;
38547 this.fireEvent("visibilitychange", this, true);
38550 closeClicked : function(){
38551 if(this.activePanel){
38552 this.remove(this.activePanel);
38556 collapseClick : function(e){
38558 e.stopPropagation();
38561 e.stopPropagation();
38567 * Collapses this region.
38568 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38571 collapse : function(skipAnim, skipCheck = false){
38572 if(this.collapsed) {
38576 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38578 this.collapsed = true;
38580 this.split.el.hide();
38582 if(this.config.animate && skipAnim !== true){
38583 this.fireEvent("invalidated", this);
38584 this.animateCollapse();
38586 this.el.setLocation(-20000,-20000);
38588 this.collapsedEl.show();
38589 this.fireEvent("collapsed", this);
38590 this.fireEvent("invalidated", this);
38596 animateCollapse : function(){
38601 * Expands this region if it was previously collapsed.
38602 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38603 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38606 expand : function(e, skipAnim){
38608 e.stopPropagation();
38610 if(!this.collapsed || this.el.hasActiveFx()) {
38614 this.afterSlideIn();
38617 this.collapsed = false;
38618 if(this.config.animate && skipAnim !== true){
38619 this.animateExpand();
38623 this.split.el.show();
38625 this.collapsedEl.setLocation(-2000,-2000);
38626 this.collapsedEl.hide();
38627 this.fireEvent("invalidated", this);
38628 this.fireEvent("expanded", this);
38632 animateExpand : function(){
38636 initTabs : function()
38638 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38640 var ts = new Roo.bootstrap.panel.Tabs({
38641 el: this.bodyEl.dom,
38643 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38644 disableTooltips: this.config.disableTabTips,
38645 toolbar : this.config.toolbar
38648 if(this.config.hideTabs){
38649 ts.stripWrap.setDisplayed(false);
38652 ts.resizeTabs = this.config.resizeTabs === true;
38653 ts.minTabWidth = this.config.minTabWidth || 40;
38654 ts.maxTabWidth = this.config.maxTabWidth || 250;
38655 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38656 ts.monitorResize = false;
38657 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38658 ts.bodyEl.addClass('roo-layout-tabs-body');
38659 this.panels.each(this.initPanelAsTab, this);
38662 initPanelAsTab : function(panel){
38663 var ti = this.tabs.addTab(
38667 this.config.closeOnTab && panel.isClosable(),
38670 if(panel.tabTip !== undefined){
38671 ti.setTooltip(panel.tabTip);
38673 ti.on("activate", function(){
38674 this.setActivePanel(panel);
38677 if(this.config.closeOnTab){
38678 ti.on("beforeclose", function(t, e){
38680 this.remove(panel);
38684 panel.tabItem = ti;
38689 updatePanelTitle : function(panel, title)
38691 if(this.activePanel == panel){
38692 this.updateTitle(title);
38695 var ti = this.tabs.getTab(panel.getEl().id);
38697 if(panel.tabTip !== undefined){
38698 ti.setTooltip(panel.tabTip);
38703 updateTitle : function(title){
38704 if(this.titleTextEl && !this.config.title){
38705 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38709 setActivePanel : function(panel)
38711 panel = this.getPanel(panel);
38712 if(this.activePanel && this.activePanel != panel){
38713 if(this.activePanel.setActiveState(false) === false){
38717 this.activePanel = panel;
38718 panel.setActiveState(true);
38719 if(this.panelSize){
38720 panel.setSize(this.panelSize.width, this.panelSize.height);
38723 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38725 this.updateTitle(panel.getTitle());
38727 this.fireEvent("invalidated", this);
38729 this.fireEvent("panelactivated", this, panel);
38733 * Shows the specified panel.
38734 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38735 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38737 showPanel : function(panel)
38739 panel = this.getPanel(panel);
38742 var tab = this.tabs.getTab(panel.getEl().id);
38743 if(tab.isHidden()){
38744 this.tabs.unhideTab(tab.id);
38748 this.setActivePanel(panel);
38755 * Get the active panel for this region.
38756 * @return {Roo.ContentPanel} The active panel or null
38758 getActivePanel : function(){
38759 return this.activePanel;
38762 validateVisibility : function(){
38763 if(this.panels.getCount() < 1){
38764 this.updateTitle(" ");
38765 this.closeBtn.hide();
38768 if(!this.isVisible()){
38775 * Adds the passed ContentPanel(s) to this region.
38776 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38777 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38779 add : function(panel)
38781 if(arguments.length > 1){
38782 for(var i = 0, len = arguments.length; i < len; i++) {
38783 this.add(arguments[i]);
38788 // if we have not been rendered yet, then we can not really do much of this..
38789 if (!this.bodyEl) {
38790 this.unrendered_panels.push(panel);
38797 if(this.hasPanel(panel)){
38798 this.showPanel(panel);
38801 panel.setRegion(this);
38802 this.panels.add(panel);
38803 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38804 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38805 // and hide them... ???
38806 this.bodyEl.dom.appendChild(panel.getEl().dom);
38807 if(panel.background !== true){
38808 this.setActivePanel(panel);
38810 this.fireEvent("paneladded", this, panel);
38817 this.initPanelAsTab(panel);
38821 if(panel.background !== true){
38822 this.tabs.activate(panel.getEl().id);
38824 this.fireEvent("paneladded", this, panel);
38829 * Hides the tab for the specified panel.
38830 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38832 hidePanel : function(panel){
38833 if(this.tabs && (panel = this.getPanel(panel))){
38834 this.tabs.hideTab(panel.getEl().id);
38839 * Unhides the tab for a previously hidden panel.
38840 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38842 unhidePanel : function(panel){
38843 if(this.tabs && (panel = this.getPanel(panel))){
38844 this.tabs.unhideTab(panel.getEl().id);
38848 clearPanels : function(){
38849 while(this.panels.getCount() > 0){
38850 this.remove(this.panels.first());
38855 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38856 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38857 * @param {Boolean} preservePanel Overrides the config preservePanel option
38858 * @return {Roo.ContentPanel} The panel that was removed
38860 remove : function(panel, preservePanel)
38862 panel = this.getPanel(panel);
38867 this.fireEvent("beforeremove", this, panel, e);
38868 if(e.cancel === true){
38871 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38872 var panelId = panel.getId();
38873 this.panels.removeKey(panelId);
38875 document.body.appendChild(panel.getEl().dom);
38878 this.tabs.removeTab(panel.getEl().id);
38879 }else if (!preservePanel){
38880 this.bodyEl.dom.removeChild(panel.getEl().dom);
38882 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38883 var p = this.panels.first();
38884 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38885 tempEl.appendChild(p.getEl().dom);
38886 this.bodyEl.update("");
38887 this.bodyEl.dom.appendChild(p.getEl().dom);
38889 this.updateTitle(p.getTitle());
38891 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38892 this.setActivePanel(p);
38894 panel.setRegion(null);
38895 if(this.activePanel == panel){
38896 this.activePanel = null;
38898 if(this.config.autoDestroy !== false && preservePanel !== true){
38899 try{panel.destroy();}catch(e){}
38901 this.fireEvent("panelremoved", this, panel);
38906 * Returns the TabPanel component used by this region
38907 * @return {Roo.TabPanel}
38909 getTabs : function(){
38913 createTool : function(parentEl, className){
38914 var btn = Roo.DomHelper.append(parentEl, {
38916 cls: "x-layout-tools-button",
38919 cls: "roo-layout-tools-button-inner " + className,
38923 btn.addClassOnOver("roo-layout-tools-button-over");
38928 * Ext JS Library 1.1.1
38929 * Copyright(c) 2006-2007, Ext JS, LLC.
38931 * Originally Released Under LGPL - original licence link has changed is not relivant.
38934 * <script type="text/javascript">
38940 * @class Roo.SplitLayoutRegion
38941 * @extends Roo.LayoutRegion
38942 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38944 Roo.bootstrap.layout.Split = function(config){
38945 this.cursor = config.cursor;
38946 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38949 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38951 splitTip : "Drag to resize.",
38952 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38953 useSplitTips : false,
38955 applyConfig : function(config){
38956 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38959 onRender : function(ctr,pos) {
38961 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38962 if(!this.config.split){
38967 var splitEl = Roo.DomHelper.append(ctr.dom, {
38969 id: this.el.id + "-split",
38970 cls: "roo-layout-split roo-layout-split-"+this.position,
38973 /** The SplitBar for this region
38974 * @type Roo.SplitBar */
38975 // does not exist yet...
38976 Roo.log([this.position, this.orientation]);
38978 this.split = new Roo.bootstrap.SplitBar({
38979 dragElement : splitEl,
38980 resizingElement: this.el,
38981 orientation : this.orientation
38984 this.split.on("moved", this.onSplitMove, this);
38985 this.split.useShim = this.config.useShim === true;
38986 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38987 if(this.useSplitTips){
38988 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38990 //if(config.collapsible){
38991 // this.split.el.on("dblclick", this.collapse, this);
38994 if(typeof this.config.minSize != "undefined"){
38995 this.split.minSize = this.config.minSize;
38997 if(typeof this.config.maxSize != "undefined"){
38998 this.split.maxSize = this.config.maxSize;
39000 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39001 this.hideSplitter();
39006 getHMaxSize : function(){
39007 var cmax = this.config.maxSize || 10000;
39008 var center = this.mgr.getRegion("center");
39009 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39012 getVMaxSize : function(){
39013 var cmax = this.config.maxSize || 10000;
39014 var center = this.mgr.getRegion("center");
39015 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39018 onSplitMove : function(split, newSize){
39019 this.fireEvent("resized", this, newSize);
39023 * Returns the {@link Roo.SplitBar} for this region.
39024 * @return {Roo.SplitBar}
39026 getSplitBar : function(){
39031 this.hideSplitter();
39032 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39035 hideSplitter : function(){
39037 this.split.el.setLocation(-2000,-2000);
39038 this.split.el.hide();
39044 this.split.el.show();
39046 Roo.bootstrap.layout.Split.superclass.show.call(this);
39049 beforeSlide: function(){
39050 if(Roo.isGecko){// firefox overflow auto bug workaround
39051 this.bodyEl.clip();
39053 this.tabs.bodyEl.clip();
39055 if(this.activePanel){
39056 this.activePanel.getEl().clip();
39058 if(this.activePanel.beforeSlide){
39059 this.activePanel.beforeSlide();
39065 afterSlide : function(){
39066 if(Roo.isGecko){// firefox overflow auto bug workaround
39067 this.bodyEl.unclip();
39069 this.tabs.bodyEl.unclip();
39071 if(this.activePanel){
39072 this.activePanel.getEl().unclip();
39073 if(this.activePanel.afterSlide){
39074 this.activePanel.afterSlide();
39080 initAutoHide : function(){
39081 if(this.autoHide !== false){
39082 if(!this.autoHideHd){
39083 var st = new Roo.util.DelayedTask(this.slideIn, this);
39084 this.autoHideHd = {
39085 "mouseout": function(e){
39086 if(!e.within(this.el, true)){
39090 "mouseover" : function(e){
39096 this.el.on(this.autoHideHd);
39100 clearAutoHide : function(){
39101 if(this.autoHide !== false){
39102 this.el.un("mouseout", this.autoHideHd.mouseout);
39103 this.el.un("mouseover", this.autoHideHd.mouseover);
39107 clearMonitor : function(){
39108 Roo.get(document).un("click", this.slideInIf, this);
39111 // these names are backwards but not changed for compat
39112 slideOut : function(){
39113 if(this.isSlid || this.el.hasActiveFx()){
39116 this.isSlid = true;
39117 if(this.collapseBtn){
39118 this.collapseBtn.hide();
39120 this.closeBtnState = this.closeBtn.getStyle('display');
39121 this.closeBtn.hide();
39123 this.stickBtn.show();
39126 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39127 this.beforeSlide();
39128 this.el.setStyle("z-index", 10001);
39129 this.el.slideIn(this.getSlideAnchor(), {
39130 callback: function(){
39132 this.initAutoHide();
39133 Roo.get(document).on("click", this.slideInIf, this);
39134 this.fireEvent("slideshow", this);
39141 afterSlideIn : function(){
39142 this.clearAutoHide();
39143 this.isSlid = false;
39144 this.clearMonitor();
39145 this.el.setStyle("z-index", "");
39146 if(this.collapseBtn){
39147 this.collapseBtn.show();
39149 this.closeBtn.setStyle('display', this.closeBtnState);
39151 this.stickBtn.hide();
39153 this.fireEvent("slidehide", this);
39156 slideIn : function(cb){
39157 if(!this.isSlid || this.el.hasActiveFx()){
39161 this.isSlid = false;
39162 this.beforeSlide();
39163 this.el.slideOut(this.getSlideAnchor(), {
39164 callback: function(){
39165 this.el.setLeftTop(-10000, -10000);
39167 this.afterSlideIn();
39175 slideInIf : function(e){
39176 if(!e.within(this.el)){
39181 animateCollapse : function(){
39182 this.beforeSlide();
39183 this.el.setStyle("z-index", 20000);
39184 var anchor = this.getSlideAnchor();
39185 this.el.slideOut(anchor, {
39186 callback : function(){
39187 this.el.setStyle("z-index", "");
39188 this.collapsedEl.slideIn(anchor, {duration:.3});
39190 this.el.setLocation(-10000,-10000);
39192 this.fireEvent("collapsed", this);
39199 animateExpand : function(){
39200 this.beforeSlide();
39201 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39202 this.el.setStyle("z-index", 20000);
39203 this.collapsedEl.hide({
39206 this.el.slideIn(this.getSlideAnchor(), {
39207 callback : function(){
39208 this.el.setStyle("z-index", "");
39211 this.split.el.show();
39213 this.fireEvent("invalidated", this);
39214 this.fireEvent("expanded", this);
39242 getAnchor : function(){
39243 return this.anchors[this.position];
39246 getCollapseAnchor : function(){
39247 return this.canchors[this.position];
39250 getSlideAnchor : function(){
39251 return this.sanchors[this.position];
39254 getAlignAdj : function(){
39255 var cm = this.cmargins;
39256 switch(this.position){
39272 getExpandAdj : function(){
39273 var c = this.collapsedEl, cm = this.cmargins;
39274 switch(this.position){
39276 return [-(cm.right+c.getWidth()+cm.left), 0];
39279 return [cm.right+c.getWidth()+cm.left, 0];
39282 return [0, -(cm.top+cm.bottom+c.getHeight())];
39285 return [0, cm.top+cm.bottom+c.getHeight()];
39291 * Ext JS Library 1.1.1
39292 * Copyright(c) 2006-2007, Ext JS, LLC.
39294 * Originally Released Under LGPL - original licence link has changed is not relivant.
39297 * <script type="text/javascript">
39300 * These classes are private internal classes
39302 Roo.bootstrap.layout.Center = function(config){
39303 config.region = "center";
39304 Roo.bootstrap.layout.Region.call(this, config);
39305 this.visible = true;
39306 this.minWidth = config.minWidth || 20;
39307 this.minHeight = config.minHeight || 20;
39310 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39312 // center panel can't be hidden
39316 // center panel can't be hidden
39319 getMinWidth: function(){
39320 return this.minWidth;
39323 getMinHeight: function(){
39324 return this.minHeight;
39338 Roo.bootstrap.layout.North = function(config)
39340 config.region = 'north';
39341 config.cursor = 'n-resize';
39343 Roo.bootstrap.layout.Split.call(this, config);
39347 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39348 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39349 this.split.el.addClass("roo-layout-split-v");
39351 //var size = config.initialSize || config.height;
39352 //if(this.el && typeof size != "undefined"){
39353 // this.el.setHeight(size);
39356 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39358 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39361 onRender : function(ctr, pos)
39363 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39364 var size = this.config.initialSize || this.config.height;
39365 if(this.el && typeof size != "undefined"){
39366 this.el.setHeight(size);
39371 getBox : function(){
39372 if(this.collapsed){
39373 return this.collapsedEl.getBox();
39375 var box = this.el.getBox();
39377 box.height += this.split.el.getHeight();
39382 updateBox : function(box){
39383 if(this.split && !this.collapsed){
39384 box.height -= this.split.el.getHeight();
39385 this.split.el.setLeft(box.x);
39386 this.split.el.setTop(box.y+box.height);
39387 this.split.el.setWidth(box.width);
39389 if(this.collapsed){
39390 this.updateBody(box.width, null);
39392 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39400 Roo.bootstrap.layout.South = function(config){
39401 config.region = 'south';
39402 config.cursor = 's-resize';
39403 Roo.bootstrap.layout.Split.call(this, config);
39405 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39406 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39407 this.split.el.addClass("roo-layout-split-v");
39412 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39413 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39415 onRender : function(ctr, pos)
39417 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39418 var size = this.config.initialSize || this.config.height;
39419 if(this.el && typeof size != "undefined"){
39420 this.el.setHeight(size);
39425 getBox : function(){
39426 if(this.collapsed){
39427 return this.collapsedEl.getBox();
39429 var box = this.el.getBox();
39431 var sh = this.split.el.getHeight();
39438 updateBox : function(box){
39439 if(this.split && !this.collapsed){
39440 var sh = this.split.el.getHeight();
39443 this.split.el.setLeft(box.x);
39444 this.split.el.setTop(box.y-sh);
39445 this.split.el.setWidth(box.width);
39447 if(this.collapsed){
39448 this.updateBody(box.width, null);
39450 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39454 Roo.bootstrap.layout.East = function(config){
39455 config.region = "east";
39456 config.cursor = "e-resize";
39457 Roo.bootstrap.layout.Split.call(this, config);
39459 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39460 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39461 this.split.el.addClass("roo-layout-split-h");
39465 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39466 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39468 onRender : function(ctr, pos)
39470 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39471 var size = this.config.initialSize || this.config.width;
39472 if(this.el && typeof size != "undefined"){
39473 this.el.setWidth(size);
39478 getBox : function(){
39479 if(this.collapsed){
39480 return this.collapsedEl.getBox();
39482 var box = this.el.getBox();
39484 var sw = this.split.el.getWidth();
39491 updateBox : function(box){
39492 if(this.split && !this.collapsed){
39493 var sw = this.split.el.getWidth();
39495 this.split.el.setLeft(box.x);
39496 this.split.el.setTop(box.y);
39497 this.split.el.setHeight(box.height);
39500 if(this.collapsed){
39501 this.updateBody(null, box.height);
39503 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39507 Roo.bootstrap.layout.West = function(config){
39508 config.region = "west";
39509 config.cursor = "w-resize";
39511 Roo.bootstrap.layout.Split.call(this, config);
39513 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39514 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39515 this.split.el.addClass("roo-layout-split-h");
39519 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39520 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39522 onRender: function(ctr, pos)
39524 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39525 var size = this.config.initialSize || this.config.width;
39526 if(typeof size != "undefined"){
39527 this.el.setWidth(size);
39531 getBox : function(){
39532 if(this.collapsed){
39533 return this.collapsedEl.getBox();
39535 var box = this.el.getBox();
39536 if (box.width == 0) {
39537 box.width = this.config.width; // kludge?
39540 box.width += this.split.el.getWidth();
39545 updateBox : function(box){
39546 if(this.split && !this.collapsed){
39547 var sw = this.split.el.getWidth();
39549 this.split.el.setLeft(box.x+box.width);
39550 this.split.el.setTop(box.y);
39551 this.split.el.setHeight(box.height);
39553 if(this.collapsed){
39554 this.updateBody(null, box.height);
39556 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39558 });Roo.namespace("Roo.bootstrap.panel");/*
39560 * Ext JS Library 1.1.1
39561 * Copyright(c) 2006-2007, Ext JS, LLC.
39563 * Originally Released Under LGPL - original licence link has changed is not relivant.
39566 * <script type="text/javascript">
39569 * @class Roo.ContentPanel
39570 * @extends Roo.util.Observable
39571 * A basic ContentPanel element.
39572 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39573 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39574 * @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
39575 * @cfg {Boolean} closable True if the panel can be closed/removed
39576 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39577 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39578 * @cfg {Toolbar} toolbar A toolbar for this panel
39579 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39580 * @cfg {String} title The title for this panel
39581 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39582 * @cfg {String} url Calls {@link #setUrl} with this value
39583 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39584 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39585 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39586 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39587 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39588 * @cfg {Boolean} badges render the badges
39589 * @cfg {String} cls extra classes to use
39590 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39593 * Create a new ContentPanel.
39594 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39595 * @param {String/Object} config A string to set only the title or a config object
39596 * @param {String} content (optional) Set the HTML content for this panel
39597 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39599 Roo.bootstrap.panel.Content = function( config){
39601 this.tpl = config.tpl || false;
39603 var el = config.el;
39604 var content = config.content;
39606 if(config.autoCreate){ // xtype is available if this is called from factory
39609 this.el = Roo.get(el);
39610 if(!this.el && config && config.autoCreate){
39611 if(typeof config.autoCreate == "object"){
39612 if(!config.autoCreate.id){
39613 config.autoCreate.id = config.id||el;
39615 this.el = Roo.DomHelper.append(document.body,
39616 config.autoCreate, true);
39620 cls: (config.cls || '') +
39621 (config.background ? ' bg-' + config.background : '') +
39622 " roo-layout-inactive-content",
39625 if (config.iframe) {
39629 style : 'border: 0px',
39630 src : 'about:blank'
39636 elcfg.html = config.html;
39640 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39641 if (config.iframe) {
39642 this.iframeEl = this.el.select('iframe',true).first();
39647 this.closable = false;
39648 this.loaded = false;
39649 this.active = false;
39652 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39654 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39656 this.wrapEl = this.el; //this.el.wrap();
39658 if (config.toolbar.items) {
39659 ti = config.toolbar.items ;
39660 delete config.toolbar.items ;
39664 this.toolbar.render(this.wrapEl, 'before');
39665 for(var i =0;i < ti.length;i++) {
39666 // Roo.log(['add child', items[i]]);
39667 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39669 this.toolbar.items = nitems;
39670 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39671 delete config.toolbar;
39675 // xtype created footer. - not sure if will work as we normally have to render first..
39676 if (this.footer && !this.footer.el && this.footer.xtype) {
39677 if (!this.wrapEl) {
39678 this.wrapEl = this.el.wrap();
39681 this.footer.container = this.wrapEl.createChild();
39683 this.footer = Roo.factory(this.footer, Roo);
39688 if(typeof config == "string"){
39689 this.title = config;
39691 Roo.apply(this, config);
39695 this.resizeEl = Roo.get(this.resizeEl, true);
39697 this.resizeEl = this.el;
39699 // handle view.xtype
39707 * Fires when this panel is activated.
39708 * @param {Roo.ContentPanel} this
39712 * @event deactivate
39713 * Fires when this panel is activated.
39714 * @param {Roo.ContentPanel} this
39716 "deactivate" : true,
39720 * Fires when this panel is resized if fitToFrame is true.
39721 * @param {Roo.ContentPanel} this
39722 * @param {Number} width The width after any component adjustments
39723 * @param {Number} height The height after any component adjustments
39729 * Fires when this tab is created
39730 * @param {Roo.ContentPanel} this
39741 if(this.autoScroll && !this.iframe){
39742 this.resizeEl.setStyle("overflow", "auto");
39744 // fix randome scrolling
39745 //this.el.on('scroll', function() {
39746 // Roo.log('fix random scolling');
39747 // this.scrollTo('top',0);
39750 content = content || this.content;
39752 this.setContent(content);
39754 if(config && config.url){
39755 this.setUrl(this.url, this.params, this.loadOnce);
39760 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39762 if (this.view && typeof(this.view.xtype) != 'undefined') {
39763 this.view.el = this.el.appendChild(document.createElement("div"));
39764 this.view = Roo.factory(this.view);
39765 this.view.render && this.view.render(false, '');
39769 this.fireEvent('render', this);
39772 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39782 setRegion : function(region){
39783 this.region = region;
39784 this.setActiveClass(region && !this.background);
39788 setActiveClass: function(state)
39791 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39792 this.el.setStyle('position','relative');
39794 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39795 this.el.setStyle('position', 'absolute');
39800 * Returns the toolbar for this Panel if one was configured.
39801 * @return {Roo.Toolbar}
39803 getToolbar : function(){
39804 return this.toolbar;
39807 setActiveState : function(active)
39809 this.active = active;
39810 this.setActiveClass(active);
39812 if(this.fireEvent("deactivate", this) === false){
39817 this.fireEvent("activate", this);
39821 * Updates this panel's element (not for iframe)
39822 * @param {String} content The new content
39823 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39825 setContent : function(content, loadScripts){
39830 this.el.update(content, loadScripts);
39833 ignoreResize : function(w, h){
39834 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39837 this.lastSize = {width: w, height: h};
39842 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39843 * @return {Roo.UpdateManager} The UpdateManager
39845 getUpdateManager : function(){
39849 return this.el.getUpdateManager();
39852 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39853 * Does not work with IFRAME contents
39854 * @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:
39857 url: "your-url.php",
39858 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39859 callback: yourFunction,
39860 scope: yourObject, //(optional scope)
39863 text: "Loading...",
39869 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39870 * 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.
39871 * @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}
39872 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39873 * @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.
39874 * @return {Roo.ContentPanel} this
39882 var um = this.el.getUpdateManager();
39883 um.update.apply(um, arguments);
39889 * 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.
39890 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39891 * @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)
39892 * @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)
39893 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39895 setUrl : function(url, params, loadOnce){
39897 this.iframeEl.dom.src = url;
39901 if(this.refreshDelegate){
39902 this.removeListener("activate", this.refreshDelegate);
39904 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39905 this.on("activate", this.refreshDelegate);
39906 return this.el.getUpdateManager();
39909 _handleRefresh : function(url, params, loadOnce){
39910 if(!loadOnce || !this.loaded){
39911 var updater = this.el.getUpdateManager();
39912 updater.update(url, params, this._setLoaded.createDelegate(this));
39916 _setLoaded : function(){
39917 this.loaded = true;
39921 * Returns this panel's id
39924 getId : function(){
39929 * Returns this panel's element - used by regiosn to add.
39930 * @return {Roo.Element}
39932 getEl : function(){
39933 return this.wrapEl || this.el;
39938 adjustForComponents : function(width, height)
39940 //Roo.log('adjustForComponents ');
39941 if(this.resizeEl != this.el){
39942 width -= this.el.getFrameWidth('lr');
39943 height -= this.el.getFrameWidth('tb');
39946 var te = this.toolbar.getEl();
39947 te.setWidth(width);
39948 height -= te.getHeight();
39951 var te = this.footer.getEl();
39952 te.setWidth(width);
39953 height -= te.getHeight();
39957 if(this.adjustments){
39958 width += this.adjustments[0];
39959 height += this.adjustments[1];
39961 return {"width": width, "height": height};
39964 setSize : function(width, height){
39965 if(this.fitToFrame && !this.ignoreResize(width, height)){
39966 if(this.fitContainer && this.resizeEl != this.el){
39967 this.el.setSize(width, height);
39969 var size = this.adjustForComponents(width, height);
39971 this.iframeEl.setSize(width,height);
39974 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39975 this.fireEvent('resize', this, size.width, size.height);
39982 * Returns this panel's title
39985 getTitle : function(){
39987 if (typeof(this.title) != 'object') {
39992 for (var k in this.title) {
39993 if (!this.title.hasOwnProperty(k)) {
39997 if (k.indexOf('-') >= 0) {
39998 var s = k.split('-');
39999 for (var i = 0; i<s.length; i++) {
40000 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40003 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40010 * Set this panel's title
40011 * @param {String} title
40013 setTitle : function(title){
40014 this.title = title;
40016 this.region.updatePanelTitle(this, title);
40021 * Returns true is this panel was configured to be closable
40022 * @return {Boolean}
40024 isClosable : function(){
40025 return this.closable;
40028 beforeSlide : function(){
40030 this.resizeEl.clip();
40033 afterSlide : function(){
40035 this.resizeEl.unclip();
40039 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40040 * Will fail silently if the {@link #setUrl} method has not been called.
40041 * This does not activate the panel, just updates its content.
40043 refresh : function(){
40044 if(this.refreshDelegate){
40045 this.loaded = false;
40046 this.refreshDelegate();
40051 * Destroys this panel
40053 destroy : function(){
40054 this.el.removeAllListeners();
40055 var tempEl = document.createElement("span");
40056 tempEl.appendChild(this.el.dom);
40057 tempEl.innerHTML = "";
40063 * form - if the content panel contains a form - this is a reference to it.
40064 * @type {Roo.form.Form}
40068 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40069 * This contains a reference to it.
40075 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40085 * @param {Object} cfg Xtype definition of item to add.
40089 getChildContainer: function () {
40090 return this.getEl();
40095 var ret = new Roo.factory(cfg);
40100 if (cfg.xtype.match(/^Form$/)) {
40103 //if (this.footer) {
40104 // el = this.footer.container.insertSibling(false, 'before');
40106 el = this.el.createChild();
40109 this.form = new Roo.form.Form(cfg);
40112 if ( this.form.allItems.length) {
40113 this.form.render(el.dom);
40117 // should only have one of theses..
40118 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40119 // views.. should not be just added - used named prop 'view''
40121 cfg.el = this.el.appendChild(document.createElement("div"));
40124 var ret = new Roo.factory(cfg);
40126 ret.render && ret.render(false, ''); // render blank..
40136 * @class Roo.bootstrap.panel.Grid
40137 * @extends Roo.bootstrap.panel.Content
40139 * Create a new GridPanel.
40140 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40141 * @param {Object} config A the config object
40147 Roo.bootstrap.panel.Grid = function(config)
40151 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40152 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40154 config.el = this.wrapper;
40155 //this.el = this.wrapper;
40157 if (config.container) {
40158 // ctor'ed from a Border/panel.grid
40161 this.wrapper.setStyle("overflow", "hidden");
40162 this.wrapper.addClass('roo-grid-container');
40167 if(config.toolbar){
40168 var tool_el = this.wrapper.createChild();
40169 this.toolbar = Roo.factory(config.toolbar);
40171 if (config.toolbar.items) {
40172 ti = config.toolbar.items ;
40173 delete config.toolbar.items ;
40177 this.toolbar.render(tool_el);
40178 for(var i =0;i < ti.length;i++) {
40179 // Roo.log(['add child', items[i]]);
40180 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40182 this.toolbar.items = nitems;
40184 delete config.toolbar;
40187 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40188 config.grid.scrollBody = true;;
40189 config.grid.monitorWindowResize = false; // turn off autosizing
40190 config.grid.autoHeight = false;
40191 config.grid.autoWidth = false;
40193 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40195 if (config.background) {
40196 // render grid on panel activation (if panel background)
40197 this.on('activate', function(gp) {
40198 if (!gp.grid.rendered) {
40199 gp.grid.render(this.wrapper);
40200 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40205 this.grid.render(this.wrapper);
40206 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40209 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40210 // ??? needed ??? config.el = this.wrapper;
40215 // xtype created footer. - not sure if will work as we normally have to render first..
40216 if (this.footer && !this.footer.el && this.footer.xtype) {
40218 var ctr = this.grid.getView().getFooterPanel(true);
40219 this.footer.dataSource = this.grid.dataSource;
40220 this.footer = Roo.factory(this.footer, Roo);
40221 this.footer.render(ctr);
40231 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40232 getId : function(){
40233 return this.grid.id;
40237 * Returns the grid for this panel
40238 * @return {Roo.bootstrap.Table}
40240 getGrid : function(){
40244 setSize : function(width, height){
40245 if(!this.ignoreResize(width, height)){
40246 var grid = this.grid;
40247 var size = this.adjustForComponents(width, height);
40248 // tfoot is not a footer?
40251 var gridel = grid.getGridEl();
40252 gridel.setSize(size.width, size.height);
40254 var tbd = grid.getGridEl().select('tbody', true).first();
40255 var thd = grid.getGridEl().select('thead',true).first();
40256 var tbf= grid.getGridEl().select('tfoot', true).first();
40259 size.height -= tbf.getHeight();
40262 size.height -= thd.getHeight();
40265 tbd.setSize(size.width, size.height );
40266 // this is for the account management tab -seems to work there.
40267 var thd = grid.getGridEl().select('thead',true).first();
40269 // tbd.setSize(size.width, size.height - thd.getHeight());
40278 beforeSlide : function(){
40279 this.grid.getView().scroller.clip();
40282 afterSlide : function(){
40283 this.grid.getView().scroller.unclip();
40286 destroy : function(){
40287 this.grid.destroy();
40289 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40294 * @class Roo.bootstrap.panel.Nest
40295 * @extends Roo.bootstrap.panel.Content
40297 * Create a new Panel, that can contain a layout.Border.
40300 * @param {Roo.BorderLayout} layout The layout for this panel
40301 * @param {String/Object} config A string to set only the title or a config object
40303 Roo.bootstrap.panel.Nest = function(config)
40305 // construct with only one argument..
40306 /* FIXME - implement nicer consturctors
40307 if (layout.layout) {
40309 layout = config.layout;
40310 delete config.layout;
40312 if (layout.xtype && !layout.getEl) {
40313 // then layout needs constructing..
40314 layout = Roo.factory(layout, Roo);
40318 config.el = config.layout.getEl();
40320 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40322 config.layout.monitorWindowResize = false; // turn off autosizing
40323 this.layout = config.layout;
40324 this.layout.getEl().addClass("roo-layout-nested-layout");
40325 this.layout.parent = this;
40332 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40334 setSize : function(width, height){
40335 if(!this.ignoreResize(width, height)){
40336 var size = this.adjustForComponents(width, height);
40337 var el = this.layout.getEl();
40338 if (size.height < 1) {
40339 el.setWidth(size.width);
40341 el.setSize(size.width, size.height);
40343 var touch = el.dom.offsetWidth;
40344 this.layout.layout();
40345 // ie requires a double layout on the first pass
40346 if(Roo.isIE && !this.initialized){
40347 this.initialized = true;
40348 this.layout.layout();
40353 // activate all subpanels if not currently active..
40355 setActiveState : function(active){
40356 this.active = active;
40357 this.setActiveClass(active);
40360 this.fireEvent("deactivate", this);
40364 this.fireEvent("activate", this);
40365 // not sure if this should happen before or after..
40366 if (!this.layout) {
40367 return; // should not happen..
40370 for (var r in this.layout.regions) {
40371 reg = this.layout.getRegion(r);
40372 if (reg.getActivePanel()) {
40373 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40374 reg.setActivePanel(reg.getActivePanel());
40377 if (!reg.panels.length) {
40380 reg.showPanel(reg.getPanel(0));
40389 * Returns the nested BorderLayout for this panel
40390 * @return {Roo.BorderLayout}
40392 getLayout : function(){
40393 return this.layout;
40397 * Adds a xtype elements to the layout of the nested panel
40401 xtype : 'ContentPanel',
40408 xtype : 'NestedLayoutPanel',
40414 items : [ ... list of content panels or nested layout panels.. ]
40418 * @param {Object} cfg Xtype definition of item to add.
40420 addxtype : function(cfg) {
40421 return this.layout.addxtype(cfg);
40426 * Ext JS Library 1.1.1
40427 * Copyright(c) 2006-2007, Ext JS, LLC.
40429 * Originally Released Under LGPL - original licence link has changed is not relivant.
40432 * <script type="text/javascript">
40435 * @class Roo.TabPanel
40436 * @extends Roo.util.Observable
40437 * A lightweight tab container.
40441 // basic tabs 1, built from existing content
40442 var tabs = new Roo.TabPanel("tabs1");
40443 tabs.addTab("script", "View Script");
40444 tabs.addTab("markup", "View Markup");
40445 tabs.activate("script");
40447 // more advanced tabs, built from javascript
40448 var jtabs = new Roo.TabPanel("jtabs");
40449 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40451 // set up the UpdateManager
40452 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40453 var updater = tab2.getUpdateManager();
40454 updater.setDefaultUrl("ajax1.htm");
40455 tab2.on('activate', updater.refresh, updater, true);
40457 // Use setUrl for Ajax loading
40458 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40459 tab3.setUrl("ajax2.htm", null, true);
40462 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40465 jtabs.activate("jtabs-1");
40468 * Create a new TabPanel.
40469 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40470 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40472 Roo.bootstrap.panel.Tabs = function(config){
40474 * The container element for this TabPanel.
40475 * @type Roo.Element
40477 this.el = Roo.get(config.el);
40480 if(typeof config == "boolean"){
40481 this.tabPosition = config ? "bottom" : "top";
40483 Roo.apply(this, config);
40487 if(this.tabPosition == "bottom"){
40488 // if tabs are at the bottom = create the body first.
40489 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40490 this.el.addClass("roo-tabs-bottom");
40492 // next create the tabs holders
40494 if (this.tabPosition == "west"){
40496 var reg = this.region; // fake it..
40498 if (!reg.mgr.parent) {
40501 reg = reg.mgr.parent.region;
40503 Roo.log("got nest?");
40505 if (reg.mgr.getRegion('west')) {
40506 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40507 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40508 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40509 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40510 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40518 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40519 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40520 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40521 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40526 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40529 // finally - if tabs are at the top, then create the body last..
40530 if(this.tabPosition != "bottom"){
40531 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40532 * @type Roo.Element
40534 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40535 this.el.addClass("roo-tabs-top");
40539 this.bodyEl.setStyle("position", "relative");
40541 this.active = null;
40542 this.activateDelegate = this.activate.createDelegate(this);
40547 * Fires when the active tab changes
40548 * @param {Roo.TabPanel} this
40549 * @param {Roo.TabPanelItem} activePanel The new active tab
40553 * @event beforetabchange
40554 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40555 * @param {Roo.TabPanel} this
40556 * @param {Object} e Set cancel to true on this object to cancel the tab change
40557 * @param {Roo.TabPanelItem} tab The tab being changed to
40559 "beforetabchange" : true
40562 Roo.EventManager.onWindowResize(this.onResize, this);
40563 this.cpad = this.el.getPadding("lr");
40564 this.hiddenCount = 0;
40567 // toolbar on the tabbar support...
40568 if (this.toolbar) {
40569 alert("no toolbar support yet");
40570 this.toolbar = false;
40572 var tcfg = this.toolbar;
40573 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40574 this.toolbar = new Roo.Toolbar(tcfg);
40575 if (Roo.isSafari) {
40576 var tbl = tcfg.container.child('table', true);
40577 tbl.setAttribute('width', '100%');
40585 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40588 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40590 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40592 tabPosition : "top",
40594 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40596 currentTabWidth : 0,
40598 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40602 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40606 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40608 preferredTabWidth : 175,
40610 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40612 resizeTabs : false,
40614 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40616 monitorResize : true,
40618 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40620 toolbar : false, // set by caller..
40622 region : false, /// set by caller
40624 disableTooltips : true, // not used yet...
40627 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40628 * @param {String} id The id of the div to use <b>or create</b>
40629 * @param {String} text The text for the tab
40630 * @param {String} content (optional) Content to put in the TabPanelItem body
40631 * @param {Boolean} closable (optional) True to create a close icon on the tab
40632 * @return {Roo.TabPanelItem} The created TabPanelItem
40634 addTab : function(id, text, content, closable, tpl)
40636 var item = new Roo.bootstrap.panel.TabItem({
40640 closable : closable,
40643 this.addTabItem(item);
40645 item.setContent(content);
40651 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40652 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40653 * @return {Roo.TabPanelItem}
40655 getTab : function(id){
40656 return this.items[id];
40660 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40661 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40663 hideTab : function(id){
40664 var t = this.items[id];
40667 this.hiddenCount++;
40668 this.autoSizeTabs();
40673 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40674 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40676 unhideTab : function(id){
40677 var t = this.items[id];
40679 t.setHidden(false);
40680 this.hiddenCount--;
40681 this.autoSizeTabs();
40686 * Adds an existing {@link Roo.TabPanelItem}.
40687 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40689 addTabItem : function(item)
40691 this.items[item.id] = item;
40692 this.items.push(item);
40693 this.autoSizeTabs();
40694 // if(this.resizeTabs){
40695 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40696 // this.autoSizeTabs();
40698 // item.autoSize();
40703 * Removes a {@link Roo.TabPanelItem}.
40704 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40706 removeTab : function(id){
40707 var items = this.items;
40708 var tab = items[id];
40709 if(!tab) { return; }
40710 var index = items.indexOf(tab);
40711 if(this.active == tab && items.length > 1){
40712 var newTab = this.getNextAvailable(index);
40717 this.stripEl.dom.removeChild(tab.pnode.dom);
40718 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40719 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40721 items.splice(index, 1);
40722 delete this.items[tab.id];
40723 tab.fireEvent("close", tab);
40724 tab.purgeListeners();
40725 this.autoSizeTabs();
40728 getNextAvailable : function(start){
40729 var items = this.items;
40731 // look for a next tab that will slide over to
40732 // replace the one being removed
40733 while(index < items.length){
40734 var item = items[++index];
40735 if(item && !item.isHidden()){
40739 // if one isn't found select the previous tab (on the left)
40742 var item = items[--index];
40743 if(item && !item.isHidden()){
40751 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40752 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40754 disableTab : function(id){
40755 var tab = this.items[id];
40756 if(tab && this.active != tab){
40762 * Enables a {@link Roo.TabPanelItem} that is disabled.
40763 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40765 enableTab : function(id){
40766 var tab = this.items[id];
40771 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40772 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40773 * @return {Roo.TabPanelItem} The TabPanelItem.
40775 activate : function(id)
40777 //Roo.log('activite:' + id);
40779 var tab = this.items[id];
40783 if(tab == this.active || tab.disabled){
40787 this.fireEvent("beforetabchange", this, e, tab);
40788 if(e.cancel !== true && !tab.disabled){
40790 this.active.hide();
40792 this.active = this.items[id];
40793 this.active.show();
40794 this.fireEvent("tabchange", this, this.active);
40800 * Gets the active {@link Roo.TabPanelItem}.
40801 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40803 getActiveTab : function(){
40804 return this.active;
40808 * Updates the tab body element to fit the height of the container element
40809 * for overflow scrolling
40810 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40812 syncHeight : function(targetHeight){
40813 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40814 var bm = this.bodyEl.getMargins();
40815 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40816 this.bodyEl.setHeight(newHeight);
40820 onResize : function(){
40821 if(this.monitorResize){
40822 this.autoSizeTabs();
40827 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40829 beginUpdate : function(){
40830 this.updating = true;
40834 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40836 endUpdate : function(){
40837 this.updating = false;
40838 this.autoSizeTabs();
40842 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40844 autoSizeTabs : function()
40846 var count = this.items.length;
40847 var vcount = count - this.hiddenCount;
40850 this.stripEl.hide();
40852 this.stripEl.show();
40855 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40860 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40861 var availWidth = Math.floor(w / vcount);
40862 var b = this.stripBody;
40863 if(b.getWidth() > w){
40864 var tabs = this.items;
40865 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40866 if(availWidth < this.minTabWidth){
40867 /*if(!this.sleft){ // incomplete scrolling code
40868 this.createScrollButtons();
40871 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40874 if(this.currentTabWidth < this.preferredTabWidth){
40875 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40881 * Returns the number of tabs in this TabPanel.
40884 getCount : function(){
40885 return this.items.length;
40889 * Resizes all the tabs to the passed width
40890 * @param {Number} The new width
40892 setTabWidth : function(width){
40893 this.currentTabWidth = width;
40894 for(var i = 0, len = this.items.length; i < len; i++) {
40895 if(!this.items[i].isHidden()) {
40896 this.items[i].setWidth(width);
40902 * Destroys this TabPanel
40903 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40905 destroy : function(removeEl){
40906 Roo.EventManager.removeResizeListener(this.onResize, this);
40907 for(var i = 0, len = this.items.length; i < len; i++){
40908 this.items[i].purgeListeners();
40910 if(removeEl === true){
40911 this.el.update("");
40916 createStrip : function(container)
40918 var strip = document.createElement("nav");
40919 strip.className = Roo.bootstrap.version == 4 ?
40920 "navbar-light bg-light" :
40921 "navbar navbar-default"; //"x-tabs-wrap";
40922 container.appendChild(strip);
40926 createStripList : function(strip)
40928 // div wrapper for retard IE
40929 // returns the "tr" element.
40930 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40931 //'<div class="x-tabs-strip-wrap">'+
40932 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40933 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40934 return strip.firstChild; //.firstChild.firstChild.firstChild;
40936 createBody : function(container)
40938 var body = document.createElement("div");
40939 Roo.id(body, "tab-body");
40940 //Roo.fly(body).addClass("x-tabs-body");
40941 Roo.fly(body).addClass("tab-content");
40942 container.appendChild(body);
40945 createItemBody :function(bodyEl, id){
40946 var body = Roo.getDom(id);
40948 body = document.createElement("div");
40951 //Roo.fly(body).addClass("x-tabs-item-body");
40952 Roo.fly(body).addClass("tab-pane");
40953 bodyEl.insertBefore(body, bodyEl.firstChild);
40957 createStripElements : function(stripEl, text, closable, tpl)
40959 var td = document.createElement("li"); // was td..
40960 td.className = 'nav-item';
40962 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40965 stripEl.appendChild(td);
40967 td.className = "x-tabs-closable";
40968 if(!this.closeTpl){
40969 this.closeTpl = new Roo.Template(
40970 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40971 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40972 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40975 var el = this.closeTpl.overwrite(td, {"text": text});
40976 var close = el.getElementsByTagName("div")[0];
40977 var inner = el.getElementsByTagName("em")[0];
40978 return {"el": el, "close": close, "inner": inner};
40981 // not sure what this is..
40982 // if(!this.tabTpl){
40983 //this.tabTpl = new Roo.Template(
40984 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40985 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40987 // this.tabTpl = new Roo.Template(
40988 // '<a href="#">' +
40989 // '<span unselectable="on"' +
40990 // (this.disableTooltips ? '' : ' title="{text}"') +
40991 // ' >{text}</span></a>'
40997 var template = tpl || this.tabTpl || false;
41000 template = new Roo.Template(
41001 Roo.bootstrap.version == 4 ?
41003 '<a class="nav-link" href="#" unselectable="on"' +
41004 (this.disableTooltips ? '' : ' title="{text}"') +
41007 '<a class="nav-link" href="#">' +
41008 '<span unselectable="on"' +
41009 (this.disableTooltips ? '' : ' title="{text}"') +
41010 ' >{text}</span></a>'
41015 switch (typeof(template)) {
41019 template = new Roo.Template(template);
41025 var el = template.overwrite(td, {"text": text});
41027 var inner = el.getElementsByTagName("span")[0];
41029 return {"el": el, "inner": inner};
41037 * @class Roo.TabPanelItem
41038 * @extends Roo.util.Observable
41039 * Represents an individual item (tab plus body) in a TabPanel.
41040 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41041 * @param {String} id The id of this TabPanelItem
41042 * @param {String} text The text for the tab of this TabPanelItem
41043 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41045 Roo.bootstrap.panel.TabItem = function(config){
41047 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41048 * @type Roo.TabPanel
41050 this.tabPanel = config.panel;
41052 * The id for this TabPanelItem
41055 this.id = config.id;
41057 this.disabled = false;
41059 this.text = config.text;
41061 this.loaded = false;
41062 this.closable = config.closable;
41065 * The body element for this TabPanelItem.
41066 * @type Roo.Element
41068 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41069 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41070 this.bodyEl.setStyle("display", "block");
41071 this.bodyEl.setStyle("zoom", "1");
41072 //this.hideAction();
41074 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41076 this.el = Roo.get(els.el);
41077 this.inner = Roo.get(els.inner, true);
41078 this.textEl = Roo.bootstrap.version == 4 ?
41079 this.el : Roo.get(this.el.dom.firstChild, true);
41081 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41082 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41085 // this.el.on("mousedown", this.onTabMouseDown, this);
41086 this.el.on("click", this.onTabClick, this);
41088 if(config.closable){
41089 var c = Roo.get(els.close, true);
41090 c.dom.title = this.closeText;
41091 c.addClassOnOver("close-over");
41092 c.on("click", this.closeClick, this);
41098 * Fires when this tab becomes the active tab.
41099 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41100 * @param {Roo.TabPanelItem} this
41104 * @event beforeclose
41105 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41106 * @param {Roo.TabPanelItem} this
41107 * @param {Object} e Set cancel to true on this object to cancel the close.
41109 "beforeclose": true,
41112 * Fires when this tab is closed.
41113 * @param {Roo.TabPanelItem} this
41117 * @event deactivate
41118 * Fires when this tab is no longer the active tab.
41119 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41120 * @param {Roo.TabPanelItem} this
41122 "deactivate" : true
41124 this.hidden = false;
41126 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41129 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41131 purgeListeners : function(){
41132 Roo.util.Observable.prototype.purgeListeners.call(this);
41133 this.el.removeAllListeners();
41136 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41139 this.status_node.addClass("active");
41142 this.tabPanel.stripWrap.repaint();
41144 this.fireEvent("activate", this.tabPanel, this);
41148 * Returns true if this tab is the active tab.
41149 * @return {Boolean}
41151 isActive : function(){
41152 return this.tabPanel.getActiveTab() == this;
41156 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41159 this.status_node.removeClass("active");
41161 this.fireEvent("deactivate", this.tabPanel, this);
41164 hideAction : function(){
41165 this.bodyEl.hide();
41166 this.bodyEl.setStyle("position", "absolute");
41167 this.bodyEl.setLeft("-20000px");
41168 this.bodyEl.setTop("-20000px");
41171 showAction : function(){
41172 this.bodyEl.setStyle("position", "relative");
41173 this.bodyEl.setTop("");
41174 this.bodyEl.setLeft("");
41175 this.bodyEl.show();
41179 * Set the tooltip for the tab.
41180 * @param {String} tooltip The tab's tooltip
41182 setTooltip : function(text){
41183 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41184 this.textEl.dom.qtip = text;
41185 this.textEl.dom.removeAttribute('title');
41187 this.textEl.dom.title = text;
41191 onTabClick : function(e){
41192 e.preventDefault();
41193 this.tabPanel.activate(this.id);
41196 onTabMouseDown : function(e){
41197 e.preventDefault();
41198 this.tabPanel.activate(this.id);
41201 getWidth : function(){
41202 return this.inner.getWidth();
41205 setWidth : function(width){
41206 var iwidth = width - this.linode.getPadding("lr");
41207 this.inner.setWidth(iwidth);
41208 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41209 this.linode.setWidth(width);
41213 * Show or hide the tab
41214 * @param {Boolean} hidden True to hide or false to show.
41216 setHidden : function(hidden){
41217 this.hidden = hidden;
41218 this.linode.setStyle("display", hidden ? "none" : "");
41222 * Returns true if this tab is "hidden"
41223 * @return {Boolean}
41225 isHidden : function(){
41226 return this.hidden;
41230 * Returns the text for this tab
41233 getText : function(){
41237 autoSize : function(){
41238 //this.el.beginMeasure();
41239 this.textEl.setWidth(1);
41241 * #2804 [new] Tabs in Roojs
41242 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41244 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41245 //this.el.endMeasure();
41249 * Sets the text for the tab (Note: this also sets the tooltip text)
41250 * @param {String} text The tab's text and tooltip
41252 setText : function(text){
41254 this.textEl.update(text);
41255 this.setTooltip(text);
41256 //if(!this.tabPanel.resizeTabs){
41257 // this.autoSize();
41261 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41263 activate : function(){
41264 this.tabPanel.activate(this.id);
41268 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41270 disable : function(){
41271 if(this.tabPanel.active != this){
41272 this.disabled = true;
41273 this.status_node.addClass("disabled");
41278 * Enables this TabPanelItem if it was previously disabled.
41280 enable : function(){
41281 this.disabled = false;
41282 this.status_node.removeClass("disabled");
41286 * Sets the content for this TabPanelItem.
41287 * @param {String} content The content
41288 * @param {Boolean} loadScripts true to look for and load scripts
41290 setContent : function(content, loadScripts){
41291 this.bodyEl.update(content, loadScripts);
41295 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41296 * @return {Roo.UpdateManager} The UpdateManager
41298 getUpdateManager : function(){
41299 return this.bodyEl.getUpdateManager();
41303 * Set a URL to be used to load the content for this TabPanelItem.
41304 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41305 * @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)
41306 * @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)
41307 * @return {Roo.UpdateManager} The UpdateManager
41309 setUrl : function(url, params, loadOnce){
41310 if(this.refreshDelegate){
41311 this.un('activate', this.refreshDelegate);
41313 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41314 this.on("activate", this.refreshDelegate);
41315 return this.bodyEl.getUpdateManager();
41319 _handleRefresh : function(url, params, loadOnce){
41320 if(!loadOnce || !this.loaded){
41321 var updater = this.bodyEl.getUpdateManager();
41322 updater.update(url, params, this._setLoaded.createDelegate(this));
41327 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41328 * Will fail silently if the setUrl method has not been called.
41329 * This does not activate the panel, just updates its content.
41331 refresh : function(){
41332 if(this.refreshDelegate){
41333 this.loaded = false;
41334 this.refreshDelegate();
41339 _setLoaded : function(){
41340 this.loaded = true;
41344 closeClick : function(e){
41347 this.fireEvent("beforeclose", this, o);
41348 if(o.cancel !== true){
41349 this.tabPanel.removeTab(this.id);
41353 * The text displayed in the tooltip for the close icon.
41356 closeText : "Close this tab"
41359 * This script refer to:
41360 * Title: International Telephone Input
41361 * Author: Jack O'Connor
41362 * Code version: v12.1.12
41363 * Availability: https://github.com/jackocnr/intl-tel-input.git
41366 Roo.bootstrap.PhoneInputData = function() {
41369 "Afghanistan (افغانستان)",
41374 "Albania (Shqipëri)",
41379 "Algeria (الجزائر)",
41404 "Antigua and Barbuda",
41414 "Armenia (Հայաստան)",
41430 "Austria (Österreich)",
41435 "Azerbaijan (Azərbaycan)",
41445 "Bahrain (البحرين)",
41450 "Bangladesh (বাংলাদেশ)",
41460 "Belarus (Беларусь)",
41465 "Belgium (België)",
41495 "Bosnia and Herzegovina (Босна и Херцеговина)",
41510 "British Indian Ocean Territory",
41515 "British Virgin Islands",
41525 "Bulgaria (България)",
41535 "Burundi (Uburundi)",
41540 "Cambodia (កម្ពុជា)",
41545 "Cameroon (Cameroun)",
41554 ["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"]
41557 "Cape Verde (Kabu Verdi)",
41562 "Caribbean Netherlands",
41573 "Central African Republic (République centrafricaine)",
41593 "Christmas Island",
41599 "Cocos (Keeling) Islands",
41610 "Comoros (جزر القمر)",
41615 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41620 "Congo (Republic) (Congo-Brazzaville)",
41640 "Croatia (Hrvatska)",
41661 "Czech Republic (Česká republika)",
41666 "Denmark (Danmark)",
41681 "Dominican Republic (República Dominicana)",
41685 ["809", "829", "849"]
41703 "Equatorial Guinea (Guinea Ecuatorial)",
41723 "Falkland Islands (Islas Malvinas)",
41728 "Faroe Islands (Føroyar)",
41749 "French Guiana (Guyane française)",
41754 "French Polynesia (Polynésie française)",
41769 "Georgia (საქართველო)",
41774 "Germany (Deutschland)",
41794 "Greenland (Kalaallit Nunaat)",
41831 "Guinea-Bissau (Guiné Bissau)",
41856 "Hungary (Magyarország)",
41861 "Iceland (Ísland)",
41881 "Iraq (العراق)",
41897 "Israel (ישראל)",
41924 "Jordan (الأردن)",
41929 "Kazakhstan (Казахстан)",
41950 "Kuwait (الكويت)",
41955 "Kyrgyzstan (Кыргызстан)",
41965 "Latvia (Latvija)",
41970 "Lebanon (لبنان)",
41985 "Libya (ليبيا)",
41995 "Lithuania (Lietuva)",
42010 "Macedonia (FYROM) (Македонија)",
42015 "Madagascar (Madagasikara)",
42045 "Marshall Islands",
42055 "Mauritania (موريتانيا)",
42060 "Mauritius (Moris)",
42081 "Moldova (Republica Moldova)",
42091 "Mongolia (Монгол)",
42096 "Montenegro (Crna Gora)",
42106 "Morocco (المغرب)",
42112 "Mozambique (Moçambique)",
42117 "Myanmar (Burma) (မြန်မာ)",
42122 "Namibia (Namibië)",
42137 "Netherlands (Nederland)",
42142 "New Caledonia (Nouvelle-Calédonie)",
42177 "North Korea (조선 민주주의 인민 공화국)",
42182 "Northern Mariana Islands",
42198 "Pakistan (پاکستان)",
42208 "Palestine (فلسطين)",
42218 "Papua New Guinea",
42260 "Réunion (La Réunion)",
42266 "Romania (România)",
42282 "Saint Barthélemy",
42293 "Saint Kitts and Nevis",
42303 "Saint Martin (Saint-Martin (partie française))",
42309 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42314 "Saint Vincent and the Grenadines",
42329 "São Tomé and Príncipe (São Tomé e Príncipe)",
42334 "Saudi Arabia (المملكة العربية السعودية)",
42339 "Senegal (Sénégal)",
42369 "Slovakia (Slovensko)",
42374 "Slovenia (Slovenija)",
42384 "Somalia (Soomaaliya)",
42394 "South Korea (대한민국)",
42399 "South Sudan (جنوب السودان)",
42409 "Sri Lanka (ශ්රී ලංකාව)",
42414 "Sudan (السودان)",
42424 "Svalbard and Jan Mayen",
42435 "Sweden (Sverige)",
42440 "Switzerland (Schweiz)",
42445 "Syria (سوريا)",
42490 "Trinidad and Tobago",
42495 "Tunisia (تونس)",
42500 "Turkey (Türkiye)",
42510 "Turks and Caicos Islands",
42520 "U.S. Virgin Islands",
42530 "Ukraine (Україна)",
42535 "United Arab Emirates (الإمارات العربية المتحدة)",
42557 "Uzbekistan (Oʻzbekiston)",
42567 "Vatican City (Città del Vaticano)",
42578 "Vietnam (Việt Nam)",
42583 "Wallis and Futuna (Wallis-et-Futuna)",
42588 "Western Sahara (الصحراء الغربية)",
42594 "Yemen (اليمن)",
42618 * This script refer to:
42619 * Title: International Telephone Input
42620 * Author: Jack O'Connor
42621 * Code version: v12.1.12
42622 * Availability: https://github.com/jackocnr/intl-tel-input.git
42626 * @class Roo.bootstrap.PhoneInput
42627 * @extends Roo.bootstrap.TriggerField
42628 * An input with International dial-code selection
42630 * @cfg {String} defaultDialCode default '+852'
42631 * @cfg {Array} preferedCountries default []
42634 * Create a new PhoneInput.
42635 * @param {Object} config Configuration options
42638 Roo.bootstrap.PhoneInput = function(config) {
42639 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42642 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42644 listWidth: undefined,
42646 selectedClass: 'active',
42648 invalidClass : "has-warning",
42650 validClass: 'has-success',
42652 allowed: '0123456789',
42657 * @cfg {String} defaultDialCode The default dial code when initializing the input
42659 defaultDialCode: '+852',
42662 * @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
42664 preferedCountries: false,
42666 getAutoCreate : function()
42668 var data = Roo.bootstrap.PhoneInputData();
42669 var align = this.labelAlign || this.parentLabelAlign();
42672 this.allCountries = [];
42673 this.dialCodeMapping = [];
42675 for (var i = 0; i < data.length; i++) {
42677 this.allCountries[i] = {
42681 priority: c[3] || 0,
42682 areaCodes: c[4] || null
42684 this.dialCodeMapping[c[2]] = {
42687 priority: c[3] || 0,
42688 areaCodes: c[4] || null
42700 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42701 maxlength: this.max_length,
42702 cls : 'form-control tel-input',
42703 autocomplete: 'new-password'
42706 var hiddenInput = {
42709 cls: 'hidden-tel-input'
42713 hiddenInput.name = this.name;
42716 if (this.disabled) {
42717 input.disabled = true;
42720 var flag_container = {
42737 cls: this.hasFeedback ? 'has-feedback' : '',
42743 cls: 'dial-code-holder',
42750 cls: 'roo-select2-container input-group',
42757 if (this.fieldLabel.length) {
42760 tooltip: 'This field is required'
42766 cls: 'control-label',
42772 html: this.fieldLabel
42775 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42781 if(this.indicatorpos == 'right') {
42782 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42789 if(align == 'left') {
42797 if(this.labelWidth > 12){
42798 label.style = "width: " + this.labelWidth + 'px';
42800 if(this.labelWidth < 13 && this.labelmd == 0){
42801 this.labelmd = this.labelWidth;
42803 if(this.labellg > 0){
42804 label.cls += ' col-lg-' + this.labellg;
42805 input.cls += ' col-lg-' + (12 - this.labellg);
42807 if(this.labelmd > 0){
42808 label.cls += ' col-md-' + this.labelmd;
42809 container.cls += ' col-md-' + (12 - this.labelmd);
42811 if(this.labelsm > 0){
42812 label.cls += ' col-sm-' + this.labelsm;
42813 container.cls += ' col-sm-' + (12 - this.labelsm);
42815 if(this.labelxs > 0){
42816 label.cls += ' col-xs-' + this.labelxs;
42817 container.cls += ' col-xs-' + (12 - this.labelxs);
42827 var settings = this;
42829 ['xs','sm','md','lg'].map(function(size){
42830 if (settings[size]) {
42831 cfg.cls += ' col-' + size + '-' + settings[size];
42835 this.store = new Roo.data.Store({
42836 proxy : new Roo.data.MemoryProxy({}),
42837 reader : new Roo.data.JsonReader({
42848 'name' : 'dialCode',
42852 'name' : 'priority',
42856 'name' : 'areaCodes',
42863 if(!this.preferedCountries) {
42864 this.preferedCountries = [
42871 var p = this.preferedCountries.reverse();
42874 for (var i = 0; i < p.length; i++) {
42875 for (var j = 0; j < this.allCountries.length; j++) {
42876 if(this.allCountries[j].iso2 == p[i]) {
42877 var t = this.allCountries[j];
42878 this.allCountries.splice(j,1);
42879 this.allCountries.unshift(t);
42885 this.store.proxy.data = {
42887 data: this.allCountries
42893 initEvents : function()
42896 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42898 this.indicator = this.indicatorEl();
42899 this.flag = this.flagEl();
42900 this.dialCodeHolder = this.dialCodeHolderEl();
42902 this.trigger = this.el.select('div.flag-box',true).first();
42903 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42908 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42909 _this.list.setWidth(lw);
42912 this.list.on('mouseover', this.onViewOver, this);
42913 this.list.on('mousemove', this.onViewMove, this);
42914 this.inputEl().on("keyup", this.onKeyUp, this);
42915 this.inputEl().on("keypress", this.onKeyPress, this);
42917 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42919 this.view = new Roo.View(this.list, this.tpl, {
42920 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42923 this.view.on('click', this.onViewClick, this);
42924 this.setValue(this.defaultDialCode);
42927 onTriggerClick : function(e)
42929 Roo.log('trigger click');
42934 if(this.isExpanded()){
42936 this.hasFocus = false;
42938 this.store.load({});
42939 this.hasFocus = true;
42944 isExpanded : function()
42946 return this.list.isVisible();
42949 collapse : function()
42951 if(!this.isExpanded()){
42955 Roo.get(document).un('mousedown', this.collapseIf, this);
42956 Roo.get(document).un('mousewheel', this.collapseIf, this);
42957 this.fireEvent('collapse', this);
42961 expand : function()
42965 if(this.isExpanded() || !this.hasFocus){
42969 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42970 this.list.setWidth(lw);
42973 this.restrictHeight();
42975 Roo.get(document).on('mousedown', this.collapseIf, this);
42976 Roo.get(document).on('mousewheel', this.collapseIf, this);
42978 this.fireEvent('expand', this);
42981 restrictHeight : function()
42983 this.list.alignTo(this.inputEl(), this.listAlign);
42984 this.list.alignTo(this.inputEl(), this.listAlign);
42987 onViewOver : function(e, t)
42989 if(this.inKeyMode){
42992 var item = this.view.findItemFromChild(t);
42995 var index = this.view.indexOf(item);
42996 this.select(index, false);
43001 onViewClick : function(view, doFocus, el, e)
43003 var index = this.view.getSelectedIndexes()[0];
43005 var r = this.store.getAt(index);
43008 this.onSelect(r, index);
43010 if(doFocus !== false && !this.blockFocus){
43011 this.inputEl().focus();
43015 onViewMove : function(e, t)
43017 this.inKeyMode = false;
43020 select : function(index, scrollIntoView)
43022 this.selectedIndex = index;
43023 this.view.select(index);
43024 if(scrollIntoView !== false){
43025 var el = this.view.getNode(index);
43027 this.list.scrollChildIntoView(el, false);
43032 createList : function()
43034 this.list = Roo.get(document.body).createChild({
43036 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43037 style: 'display:none'
43040 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43043 collapseIf : function(e)
43045 var in_combo = e.within(this.el);
43046 var in_list = e.within(this.list);
43047 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43049 if (in_combo || in_list || is_list) {
43055 onSelect : function(record, index)
43057 if(this.fireEvent('beforeselect', this, record, index) !== false){
43059 this.setFlagClass(record.data.iso2);
43060 this.setDialCode(record.data.dialCode);
43061 this.hasFocus = false;
43063 this.fireEvent('select', this, record, index);
43067 flagEl : function()
43069 var flag = this.el.select('div.flag',true).first();
43076 dialCodeHolderEl : function()
43078 var d = this.el.select('input.dial-code-holder',true).first();
43085 setDialCode : function(v)
43087 this.dialCodeHolder.dom.value = '+'+v;
43090 setFlagClass : function(n)
43092 this.flag.dom.className = 'flag '+n;
43095 getValue : function()
43097 var v = this.inputEl().getValue();
43098 if(this.dialCodeHolder) {
43099 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43104 setValue : function(v)
43106 var d = this.getDialCode(v);
43108 //invalid dial code
43109 if(v.length == 0 || !d || d.length == 0) {
43111 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43112 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43118 this.setFlagClass(this.dialCodeMapping[d].iso2);
43119 this.setDialCode(d);
43120 this.inputEl().dom.value = v.replace('+'+d,'');
43121 this.hiddenEl().dom.value = this.getValue();
43126 getDialCode : function(v)
43130 if (v.length == 0) {
43131 return this.dialCodeHolder.dom.value;
43135 if (v.charAt(0) != "+") {
43138 var numericChars = "";
43139 for (var i = 1; i < v.length; i++) {
43140 var c = v.charAt(i);
43143 if (this.dialCodeMapping[numericChars]) {
43144 dialCode = v.substr(1, i);
43146 if (numericChars.length == 4) {
43156 this.setValue(this.defaultDialCode);
43160 hiddenEl : function()
43162 return this.el.select('input.hidden-tel-input',true).first();
43165 // after setting val
43166 onKeyUp : function(e){
43167 this.setValue(this.getValue());
43170 onKeyPress : function(e){
43171 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43178 * @class Roo.bootstrap.MoneyField
43179 * @extends Roo.bootstrap.ComboBox
43180 * Bootstrap MoneyField class
43183 * Create a new MoneyField.
43184 * @param {Object} config Configuration options
43187 Roo.bootstrap.MoneyField = function(config) {
43189 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43193 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43196 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43198 allowDecimals : true,
43200 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43202 decimalSeparator : ".",
43204 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43206 decimalPrecision : 0,
43208 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43210 allowNegative : true,
43212 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43216 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43218 minValue : Number.NEGATIVE_INFINITY,
43220 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43222 maxValue : Number.MAX_VALUE,
43224 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43226 minText : "The minimum value for this field is {0}",
43228 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43230 maxText : "The maximum value for this field is {0}",
43232 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43233 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43235 nanText : "{0} is not a valid number",
43237 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43241 * @cfg {String} defaults currency of the MoneyField
43242 * value should be in lkey
43244 defaultCurrency : false,
43246 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43248 thousandsDelimiter : false,
43250 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43261 getAutoCreate : function()
43263 var align = this.labelAlign || this.parentLabelAlign();
43275 cls : 'form-control roo-money-amount-input',
43276 autocomplete: 'new-password'
43279 var hiddenInput = {
43283 cls: 'hidden-number-input'
43286 if(this.max_length) {
43287 input.maxlength = this.max_length;
43291 hiddenInput.name = this.name;
43294 if (this.disabled) {
43295 input.disabled = true;
43298 var clg = 12 - this.inputlg;
43299 var cmd = 12 - this.inputmd;
43300 var csm = 12 - this.inputsm;
43301 var cxs = 12 - this.inputxs;
43305 cls : 'row roo-money-field',
43309 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43313 cls: 'roo-select2-container input-group',
43317 cls : 'form-control roo-money-currency-input',
43318 autocomplete: 'new-password',
43320 name : this.currencyName
43324 cls : 'input-group-addon',
43338 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43342 cls: this.hasFeedback ? 'has-feedback' : '',
43353 if (this.fieldLabel.length) {
43356 tooltip: 'This field is required'
43362 cls: 'control-label',
43368 html: this.fieldLabel
43371 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43377 if(this.indicatorpos == 'right') {
43378 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43385 if(align == 'left') {
43393 if(this.labelWidth > 12){
43394 label.style = "width: " + this.labelWidth + 'px';
43396 if(this.labelWidth < 13 && this.labelmd == 0){
43397 this.labelmd = this.labelWidth;
43399 if(this.labellg > 0){
43400 label.cls += ' col-lg-' + this.labellg;
43401 input.cls += ' col-lg-' + (12 - this.labellg);
43403 if(this.labelmd > 0){
43404 label.cls += ' col-md-' + this.labelmd;
43405 container.cls += ' col-md-' + (12 - this.labelmd);
43407 if(this.labelsm > 0){
43408 label.cls += ' col-sm-' + this.labelsm;
43409 container.cls += ' col-sm-' + (12 - this.labelsm);
43411 if(this.labelxs > 0){
43412 label.cls += ' col-xs-' + this.labelxs;
43413 container.cls += ' col-xs-' + (12 - this.labelxs);
43424 var settings = this;
43426 ['xs','sm','md','lg'].map(function(size){
43427 if (settings[size]) {
43428 cfg.cls += ' col-' + size + '-' + settings[size];
43435 initEvents : function()
43437 this.indicator = this.indicatorEl();
43439 this.initCurrencyEvent();
43441 this.initNumberEvent();
43444 initCurrencyEvent : function()
43447 throw "can not find store for combo";
43450 this.store = Roo.factory(this.store, Roo.data);
43451 this.store.parent = this;
43455 this.triggerEl = this.el.select('.input-group-addon', true).first();
43457 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43462 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43463 _this.list.setWidth(lw);
43466 this.list.on('mouseover', this.onViewOver, this);
43467 this.list.on('mousemove', this.onViewMove, this);
43468 this.list.on('scroll', this.onViewScroll, this);
43471 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43474 this.view = new Roo.View(this.list, this.tpl, {
43475 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43478 this.view.on('click', this.onViewClick, this);
43480 this.store.on('beforeload', this.onBeforeLoad, this);
43481 this.store.on('load', this.onLoad, this);
43482 this.store.on('loadexception', this.onLoadException, this);
43484 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43485 "up" : function(e){
43486 this.inKeyMode = true;
43490 "down" : function(e){
43491 if(!this.isExpanded()){
43492 this.onTriggerClick();
43494 this.inKeyMode = true;
43499 "enter" : function(e){
43502 if(this.fireEvent("specialkey", this, e)){
43503 this.onViewClick(false);
43509 "esc" : function(e){
43513 "tab" : function(e){
43516 if(this.fireEvent("specialkey", this, e)){
43517 this.onViewClick(false);
43525 doRelay : function(foo, bar, hname){
43526 if(hname == 'down' || this.scope.isExpanded()){
43527 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43535 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43539 initNumberEvent : function(e)
43541 this.inputEl().on("keydown" , this.fireKey, this);
43542 this.inputEl().on("focus", this.onFocus, this);
43543 this.inputEl().on("blur", this.onBlur, this);
43545 this.inputEl().relayEvent('keyup', this);
43547 if(this.indicator){
43548 this.indicator.addClass('invisible');
43551 this.originalValue = this.getValue();
43553 if(this.validationEvent == 'keyup'){
43554 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43555 this.inputEl().on('keyup', this.filterValidation, this);
43557 else if(this.validationEvent !== false){
43558 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43561 if(this.selectOnFocus){
43562 this.on("focus", this.preFocus, this);
43565 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43566 this.inputEl().on("keypress", this.filterKeys, this);
43568 this.inputEl().relayEvent('keypress', this);
43571 var allowed = "0123456789";
43573 if(this.allowDecimals){
43574 allowed += this.decimalSeparator;
43577 if(this.allowNegative){
43581 if(this.thousandsDelimiter) {
43585 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43587 var keyPress = function(e){
43589 var k = e.getKey();
43591 var c = e.getCharCode();
43594 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43595 allowed.indexOf(String.fromCharCode(c)) === -1
43601 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43605 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43610 this.inputEl().on("keypress", keyPress, this);
43614 onTriggerClick : function(e)
43621 this.loadNext = false;
43623 if(this.isExpanded()){
43628 this.hasFocus = true;
43630 if(this.triggerAction == 'all') {
43631 this.doQuery(this.allQuery, true);
43635 this.doQuery(this.getRawValue());
43638 getCurrency : function()
43640 var v = this.currencyEl().getValue();
43645 restrictHeight : function()
43647 this.list.alignTo(this.currencyEl(), this.listAlign);
43648 this.list.alignTo(this.currencyEl(), this.listAlign);
43651 onViewClick : function(view, doFocus, el, e)
43653 var index = this.view.getSelectedIndexes()[0];
43655 var r = this.store.getAt(index);
43658 this.onSelect(r, index);
43662 onSelect : function(record, index){
43664 if(this.fireEvent('beforeselect', this, record, index) !== false){
43666 this.setFromCurrencyData(index > -1 ? record.data : false);
43670 this.fireEvent('select', this, record, index);
43674 setFromCurrencyData : function(o)
43678 this.lastCurrency = o;
43680 if (this.currencyField) {
43681 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43683 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43686 this.lastSelectionText = currency;
43688 //setting default currency
43689 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43690 this.setCurrency(this.defaultCurrency);
43694 this.setCurrency(currency);
43697 setFromData : function(o)
43701 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43703 this.setFromCurrencyData(c);
43708 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43710 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43713 this.setValue(value);
43717 setCurrency : function(v)
43719 this.currencyValue = v;
43722 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43727 setValue : function(v)
43729 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43735 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43737 this.inputEl().dom.value = (v == '') ? '' :
43738 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43740 if(!this.allowZero && v === '0') {
43741 this.hiddenEl().dom.value = '';
43742 this.inputEl().dom.value = '';
43749 getRawValue : function()
43751 var v = this.inputEl().getValue();
43756 getValue : function()
43758 return this.fixPrecision(this.parseValue(this.getRawValue()));
43761 parseValue : function(value)
43763 if(this.thousandsDelimiter) {
43765 r = new RegExp(",", "g");
43766 value = value.replace(r, "");
43769 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43770 return isNaN(value) ? '' : value;
43774 fixPrecision : function(value)
43776 if(this.thousandsDelimiter) {
43778 r = new RegExp(",", "g");
43779 value = value.replace(r, "");
43782 var nan = isNaN(value);
43784 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43785 return nan ? '' : value;
43787 return parseFloat(value).toFixed(this.decimalPrecision);
43790 decimalPrecisionFcn : function(v)
43792 return Math.floor(v);
43795 validateValue : function(value)
43797 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43801 var num = this.parseValue(value);
43804 this.markInvalid(String.format(this.nanText, value));
43808 if(num < this.minValue){
43809 this.markInvalid(String.format(this.minText, this.minValue));
43813 if(num > this.maxValue){
43814 this.markInvalid(String.format(this.maxText, this.maxValue));
43821 validate : function()
43823 if(this.disabled || this.allowBlank){
43828 var currency = this.getCurrency();
43830 if(this.validateValue(this.getRawValue()) && currency.length){
43835 this.markInvalid();
43839 getName: function()
43844 beforeBlur : function()
43850 var v = this.parseValue(this.getRawValue());
43857 onBlur : function()
43861 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43862 //this.el.removeClass(this.focusClass);
43865 this.hasFocus = false;
43867 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43871 var v = this.getValue();
43873 if(String(v) !== String(this.startValue)){
43874 this.fireEvent('change', this, v, this.startValue);
43877 this.fireEvent("blur", this);
43880 inputEl : function()
43882 return this.el.select('.roo-money-amount-input', true).first();
43885 currencyEl : function()
43887 return this.el.select('.roo-money-currency-input', true).first();
43890 hiddenEl : function()
43892 return this.el.select('input.hidden-number-input',true).first();
43896 * @class Roo.bootstrap.BezierSignature
43897 * @extends Roo.bootstrap.Component
43898 * Bootstrap BezierSignature class
43899 * This script refer to:
43900 * Title: Signature Pad
43902 * Availability: https://github.com/szimek/signature_pad
43905 * Create a new BezierSignature
43906 * @param {Object} config The config object
43909 Roo.bootstrap.BezierSignature = function(config){
43910 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43916 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43923 mouse_btn_down: true,
43926 * @cfg {int} canvas height
43928 canvas_height: '200px',
43931 * @cfg {float|function} Radius of a single dot.
43936 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43941 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43946 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43951 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43956 * @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.
43958 bg_color: 'rgba(0, 0, 0, 0)',
43961 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43963 dot_color: 'black',
43966 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43968 velocity_filter_weight: 0.7,
43971 * @cfg {function} Callback when stroke begin.
43976 * @cfg {function} Callback when stroke end.
43980 getAutoCreate : function()
43982 var cls = 'roo-signature column';
43985 cls += ' ' + this.cls;
43995 for(var i = 0; i < col_sizes.length; i++) {
43996 if(this[col_sizes[i]]) {
43997 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44007 cls: 'roo-signature-body',
44011 cls: 'roo-signature-body-canvas',
44012 height: this.canvas_height,
44013 width: this.canvas_width
44020 style: 'display: none'
44028 initEvents: function()
44030 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44032 var canvas = this.canvasEl();
44034 // mouse && touch event swapping...
44035 canvas.dom.style.touchAction = 'none';
44036 canvas.dom.style.msTouchAction = 'none';
44038 this.mouse_btn_down = false;
44039 canvas.on('mousedown', this._handleMouseDown, this);
44040 canvas.on('mousemove', this._handleMouseMove, this);
44041 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44043 if (window.PointerEvent) {
44044 canvas.on('pointerdown', this._handleMouseDown, this);
44045 canvas.on('pointermove', this._handleMouseMove, this);
44046 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44049 if ('ontouchstart' in window) {
44050 canvas.on('touchstart', this._handleTouchStart, this);
44051 canvas.on('touchmove', this._handleTouchMove, this);
44052 canvas.on('touchend', this._handleTouchEnd, this);
44055 Roo.EventManager.onWindowResize(this.resize, this, true);
44057 // file input event
44058 this.fileEl().on('change', this.uploadImage, this);
44065 resize: function(){
44067 var canvas = this.canvasEl().dom;
44068 var ctx = this.canvasElCtx();
44069 var img_data = false;
44071 if(canvas.width > 0) {
44072 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44074 // setting canvas width will clean img data
44077 var style = window.getComputedStyle ?
44078 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44080 var padding_left = parseInt(style.paddingLeft) || 0;
44081 var padding_right = parseInt(style.paddingRight) || 0;
44083 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44086 ctx.putImageData(img_data, 0, 0);
44090 _handleMouseDown: function(e)
44092 if (e.browserEvent.which === 1) {
44093 this.mouse_btn_down = true;
44094 this.strokeBegin(e);
44098 _handleMouseMove: function (e)
44100 if (this.mouse_btn_down) {
44101 this.strokeMoveUpdate(e);
44105 _handleMouseUp: function (e)
44107 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44108 this.mouse_btn_down = false;
44113 _handleTouchStart: function (e) {
44115 e.preventDefault();
44116 if (e.browserEvent.targetTouches.length === 1) {
44117 // var touch = e.browserEvent.changedTouches[0];
44118 // this.strokeBegin(touch);
44120 this.strokeBegin(e); // assume e catching the correct xy...
44124 _handleTouchMove: function (e) {
44125 e.preventDefault();
44126 // var touch = event.targetTouches[0];
44127 // _this._strokeMoveUpdate(touch);
44128 this.strokeMoveUpdate(e);
44131 _handleTouchEnd: function (e) {
44132 var wasCanvasTouched = e.target === this.canvasEl().dom;
44133 if (wasCanvasTouched) {
44134 e.preventDefault();
44135 // var touch = event.changedTouches[0];
44136 // _this._strokeEnd(touch);
44141 reset: function () {
44142 this._lastPoints = [];
44143 this._lastVelocity = 0;
44144 this._lastWidth = (this.min_width + this.max_width) / 2;
44145 this.canvasElCtx().fillStyle = this.dot_color;
44148 strokeMoveUpdate: function(e)
44150 this.strokeUpdate(e);
44152 if (this.throttle) {
44153 this.throttleStroke(this.strokeUpdate, this.throttle);
44156 this.strokeUpdate(e);
44160 strokeBegin: function(e)
44162 var newPointGroup = {
44163 color: this.dot_color,
44167 if (typeof this.onBegin === 'function') {
44171 this.curve_data.push(newPointGroup);
44173 this.strokeUpdate(e);
44176 strokeUpdate: function(e)
44178 var rect = this.canvasEl().dom.getBoundingClientRect();
44179 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44180 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44181 var lastPoints = lastPointGroup.points;
44182 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44183 var isLastPointTooClose = lastPoint
44184 ? point.distanceTo(lastPoint) <= this.min_distance
44186 var color = lastPointGroup.color;
44187 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44188 var curve = this.addPoint(point);
44190 this.drawDot({color: color, point: point});
44193 this.drawCurve({color: color, curve: curve});
44203 strokeEnd: function(e)
44205 this.strokeUpdate(e);
44206 if (typeof this.onEnd === 'function') {
44211 addPoint: function (point) {
44212 var _lastPoints = this._lastPoints;
44213 _lastPoints.push(point);
44214 if (_lastPoints.length > 2) {
44215 if (_lastPoints.length === 3) {
44216 _lastPoints.unshift(_lastPoints[0]);
44218 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44219 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44220 _lastPoints.shift();
44226 calculateCurveWidths: function (startPoint, endPoint) {
44227 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44228 (1 - this.velocity_filter_weight) * this._lastVelocity;
44230 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44233 start: this._lastWidth
44236 this._lastVelocity = velocity;
44237 this._lastWidth = newWidth;
44241 drawDot: function (_a) {
44242 var color = _a.color, point = _a.point;
44243 var ctx = this.canvasElCtx();
44244 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44246 this.drawCurveSegment(point.x, point.y, width);
44248 ctx.fillStyle = color;
44252 drawCurve: function (_a) {
44253 var color = _a.color, curve = _a.curve;
44254 var ctx = this.canvasElCtx();
44255 var widthDelta = curve.endWidth - curve.startWidth;
44256 var drawSteps = Math.floor(curve.length()) * 2;
44258 ctx.fillStyle = color;
44259 for (var i = 0; i < drawSteps; i += 1) {
44260 var t = i / drawSteps;
44266 var x = uuu * curve.startPoint.x;
44267 x += 3 * uu * t * curve.control1.x;
44268 x += 3 * u * tt * curve.control2.x;
44269 x += ttt * curve.endPoint.x;
44270 var y = uuu * curve.startPoint.y;
44271 y += 3 * uu * t * curve.control1.y;
44272 y += 3 * u * tt * curve.control2.y;
44273 y += ttt * curve.endPoint.y;
44274 var width = curve.startWidth + ttt * widthDelta;
44275 this.drawCurveSegment(x, y, width);
44281 drawCurveSegment: function (x, y, width) {
44282 var ctx = this.canvasElCtx();
44284 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44285 this.is_empty = false;
44290 var ctx = this.canvasElCtx();
44291 var canvas = this.canvasEl().dom;
44292 ctx.fillStyle = this.bg_color;
44293 ctx.clearRect(0, 0, canvas.width, canvas.height);
44294 ctx.fillRect(0, 0, canvas.width, canvas.height);
44295 this.curve_data = [];
44297 this.is_empty = true;
44302 return this.el.select('input',true).first();
44305 canvasEl: function()
44307 return this.el.select('canvas',true).first();
44310 canvasElCtx: function()
44312 return this.el.select('canvas',true).first().dom.getContext('2d');
44315 getImage: function(type)
44317 if(this.is_empty) {
44322 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44325 drawFromImage: function(img_src)
44327 var img = new Image();
44329 img.onload = function(){
44330 this.canvasElCtx().drawImage(img, 0, 0);
44335 this.is_empty = false;
44338 selectImage: function()
44340 this.fileEl().dom.click();
44343 uploadImage: function(e)
44345 var reader = new FileReader();
44347 reader.onload = function(e){
44348 var img = new Image();
44349 img.onload = function(){
44351 this.canvasElCtx().drawImage(img, 0, 0);
44353 img.src = e.target.result;
44356 reader.readAsDataURL(e.target.files[0]);
44359 // Bezier Point Constructor
44360 Point: (function () {
44361 function Point(x, y, time) {
44364 this.time = time || Date.now();
44366 Point.prototype.distanceTo = function (start) {
44367 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44369 Point.prototype.equals = function (other) {
44370 return this.x === other.x && this.y === other.y && this.time === other.time;
44372 Point.prototype.velocityFrom = function (start) {
44373 return this.time !== start.time
44374 ? this.distanceTo(start) / (this.time - start.time)
44381 // Bezier Constructor
44382 Bezier: (function () {
44383 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44384 this.startPoint = startPoint;
44385 this.control2 = control2;
44386 this.control1 = control1;
44387 this.endPoint = endPoint;
44388 this.startWidth = startWidth;
44389 this.endWidth = endWidth;
44391 Bezier.fromPoints = function (points, widths, scope) {
44392 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44393 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44394 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44396 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44397 var dx1 = s1.x - s2.x;
44398 var dy1 = s1.y - s2.y;
44399 var dx2 = s2.x - s3.x;
44400 var dy2 = s2.y - s3.y;
44401 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44402 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44403 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44404 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44405 var dxm = m1.x - m2.x;
44406 var dym = m1.y - m2.y;
44407 var k = l2 / (l1 + l2);
44408 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44409 var tx = s2.x - cm.x;
44410 var ty = s2.y - cm.y;
44412 c1: new scope.Point(m1.x + tx, m1.y + ty),
44413 c2: new scope.Point(m2.x + tx, m2.y + ty)
44416 Bezier.prototype.length = function () {
44421 for (var i = 0; i <= steps; i += 1) {
44423 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44424 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44426 var xdiff = cx - px;
44427 var ydiff = cy - py;
44428 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44435 Bezier.prototype.point = function (t, start, c1, c2, end) {
44436 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44437 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44438 + (3.0 * c2 * (1.0 - t) * t * t)
44439 + (end * t * t * t);
44444 throttleStroke: function(fn, wait) {
44445 if (wait === void 0) { wait = 250; }
44447 var timeout = null;
44451 var later = function () {
44452 previous = Date.now();
44454 result = fn.apply(storedContext, storedArgs);
44456 storedContext = null;
44460 return function wrapper() {
44462 for (var _i = 0; _i < arguments.length; _i++) {
44463 args[_i] = arguments[_i];
44465 var now = Date.now();
44466 var remaining = wait - (now - previous);
44467 storedContext = this;
44469 if (remaining <= 0 || remaining > wait) {
44471 clearTimeout(timeout);
44475 result = fn.apply(storedContext, storedArgs);
44477 storedContext = null;
44481 else if (!timeout) {
44482 timeout = window.setTimeout(later, remaining);