2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && s.href.match(/css-bootstrap4/)) {
17 * Ext JS Library 1.1.1
18 * Copyright(c) 2006-2007, Ext JS, LLC.
20 * Originally Released Under LGPL - original licence link has changed is not relivant.
23 * <script type="text/javascript">
29 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
30 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
31 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34 * @param {Object} config The config object
36 Roo.Shadow = function(config){
37 Roo.apply(this, config);
38 if(typeof this.mode != "string"){
39 this.mode = this.defaultMode;
41 var o = this.offset, a = {h: 0};
42 var rad = Math.floor(this.offset/2);
43 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49 a.l -= this.offset + rad;
50 a.t -= this.offset + rad;
61 a.l -= (this.offset - rad);
62 a.t -= this.offset + rad;
64 a.w -= (this.offset - rad)*2;
75 a.l -= (this.offset - rad);
76 a.t -= (this.offset - rad);
78 a.w -= (this.offset + rad + 1);
79 a.h -= (this.offset + rad);
88 Roo.Shadow.prototype = {
91 * The shadow display mode. Supports the following options:<br />
92 * sides: Shadow displays on both sides and bottom only<br />
93 * frame: Shadow displays equally on all four sides<br />
94 * drop: Traditional bottom-right drop shadow (default)
97 * @cfg {String} offset
98 * The number of pixels to offset the shadow from the element (defaults to 4)
106 * Displays the shadow under the target element
107 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
109 show : function(target){
110 target = Roo.get(target);
112 this.el = Roo.Shadow.Pool.pull();
113 if(this.el.dom.nextSibling != target.dom){
114 this.el.insertBefore(target);
117 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
119 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122 target.getLeft(true),
127 this.el.dom.style.display = "block";
131 * Returns true if the shadow is visible, else false
133 isVisible : function(){
134 return this.el ? true : false;
138 * Direct alignment when values are already available. Show must be called at least once before
139 * calling this method to ensure it is initialized.
140 * @param {Number} left The target element left position
141 * @param {Number} top The target element top position
142 * @param {Number} width The target element width
143 * @param {Number} height The target element height
145 realign : function(l, t, w, h){
149 var a = this.adjusts, d = this.el.dom, s = d.style;
151 s.left = (l+a.l)+"px";
152 s.top = (t+a.t)+"px";
153 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
155 if(s.width != sws || s.height != shs){
159 var cn = d.childNodes;
160 var sww = Math.max(0, (sw-12))+"px";
161 cn[0].childNodes[1].style.width = sww;
162 cn[1].childNodes[1].style.width = sww;
163 cn[2].childNodes[1].style.width = sww;
164 cn[1].style.height = Math.max(0, (sh-12))+"px";
174 this.el.dom.style.display = "none";
175 Roo.Shadow.Pool.push(this.el);
181 * Adjust the z-index of this shadow
182 * @param {Number} zindex The new z-index
184 setZIndex : function(z){
187 this.el.setStyle("z-index", z);
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
195 var markup = Roo.isIE ?
196 '<div class="x-ie-shadow"></div>' :
197 '<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>';
202 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203 sh.autoBoxAdjust = false;
215 * base class for bootstrap elements.
219 Roo.bootstrap = Roo.bootstrap || {};
221 * @class Roo.bootstrap.Component
222 * @extends Roo.Component
223 * Bootstrap Component base class
224 * @cfg {String} cls css class
225 * @cfg {String} style any extra css
226 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
228 * @cfg {string} dataId cutomer id
229 * @cfg {string} name Specifies name attribute
230 * @cfg {string} tooltip Text for the tooltip
231 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
232 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235 * Do not use directly - it does not do anything..
236 * @param {Object} config The config object
241 Roo.bootstrap.Component = function(config){
242 Roo.bootstrap.Component.superclass.constructor.call(this, config);
246 * @event childrenrendered
247 * Fires when the children have been rendered..
248 * @param {Roo.bootstrap.Component} this
250 "childrenrendered" : true
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
262 allowDomMove : false, // to stop relocations in parent onRender...
272 * Initialize Events for the element
274 initEvents : function() { },
280 can_build_overlaid : true,
282 container_method : false,
289 // returns the parent component..
290 return Roo.ComponentMgr.get(this.parentId)
296 onRender : function(ct, position)
298 // Roo.log("Call onRender: " + this.xtype);
300 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303 if (this.el.attr('xtype')) {
304 this.el.attr('xtypex', this.el.attr('xtype'));
305 this.el.dom.removeAttribute('xtype');
315 var cfg = Roo.apply({}, this.getAutoCreate());
317 cfg.id = this.id || Roo.id();
319 // fill in the extra attributes
320 if (this.xattr && typeof(this.xattr) =='object') {
321 for (var i in this.xattr) {
322 cfg[i] = this.xattr[i];
327 cfg.dataId = this.dataId;
331 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334 if (this.style) { // fixme needs to support more complex style data.
335 cfg.style = this.style;
339 cfg.name = this.name;
342 this.el = ct.createChild(cfg, position);
345 this.tooltipEl().attr('tooltip', this.tooltip);
348 if(this.tabIndex !== undefined){
349 this.el.dom.setAttribute('tabIndex', this.tabIndex);
356 * Fetch the element to add children to
357 * @return {Roo.Element} defaults to this.el
359 getChildContainer : function()
364 * Fetch the element to display the tooltip on.
365 * @return {Roo.Element} defaults to this.el
367 tooltipEl : function()
372 addxtype : function(tree,cntr)
376 cn = Roo.factory(tree);
377 //Roo.log(['addxtype', cn]);
379 cn.parentType = this.xtype; //??
380 cn.parentId = this.id;
382 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383 if (typeof(cn.container_method) == 'string') {
384 cntr = cn.container_method;
388 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
390 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
392 var build_from_html = Roo.XComponent.build_from_html;
394 var is_body = (tree.xtype == 'Body') ;
396 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
398 var self_cntr_el = Roo.get(this[cntr](false));
400 // do not try and build conditional elements
401 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
405 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407 return this.addxtypeChild(tree,cntr, is_body);
410 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416 Roo.log('skipping render');
422 if (!build_from_html) {
426 // this i think handles overlaying multiple children of the same type
427 // with the sam eelement.. - which might be buggy..
429 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
435 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
439 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
446 addxtypeChild : function (tree, cntr, is_body)
448 Roo.debug && Roo.log('addxtypeChild:' + cntr);
450 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454 (typeof(tree['flexy:foreach']) != 'undefined');
458 skip_children = false;
459 // render the element if it's not BODY.
462 // if parent was disabled, then do not try and create the children..
463 if(!this[cntr](true)){
468 cn = Roo.factory(tree);
470 cn.parentType = this.xtype; //??
471 cn.parentId = this.id;
473 var build_from_html = Roo.XComponent.build_from_html;
476 // does the container contain child eleemnts with 'xtype' attributes.
477 // that match this xtype..
478 // note - when we render we create these as well..
479 // so we should check to see if body has xtype set.
480 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
482 var self_cntr_el = Roo.get(this[cntr](false));
483 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
485 //Roo.log(Roo.XComponent.build_from_html);
486 //Roo.log("got echild:");
489 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490 // and are not displayed -this causes this to use up the wrong element when matching.
491 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
501 //echild.dom.removeAttribute('xtype');
503 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504 Roo.debug && Roo.log(self_cntr_el);
505 Roo.debug && Roo.log(echild);
506 Roo.debug && Roo.log(cn);
512 // if object has flexy:if - then it may or may not be rendered.
513 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
514 // skip a flexy if element.
515 Roo.debug && Roo.log('skipping render');
516 Roo.debug && Roo.log(tree);
518 Roo.debug && Roo.log('skipping all children');
519 skip_children = true;
524 // actually if flexy:foreach is found, we really want to create
525 // multiple copies here...
527 //Roo.log(this[cntr]());
528 // some elements do not have render methods.. like the layouts...
530 if(this[cntr](true) === false){
535 cn.render && cn.render(this[cntr](true));
538 // then add the element..
545 if (typeof (tree.menu) != 'undefined') {
546 tree.menu.parentType = cn.xtype;
547 tree.menu.triggerEl = cn.el;
548 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
552 if (!tree.items || !tree.items.length) {
554 //Roo.log(["no children", this]);
559 var items = tree.items;
562 //Roo.log(items.length);
564 if (!skip_children) {
565 for(var i =0;i < items.length;i++) {
566 // Roo.log(['add child', items[i]]);
567 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
573 //Roo.log("fire childrenrendered");
575 cn.fireEvent('childrenrendered', this);
581 * Set the element that will be used to show or hide
583 setVisibilityEl : function(el)
585 this.visibilityEl = el;
589 * Get the element that will be used to show or hide
591 getVisibilityEl : function()
593 if (typeof(this.visibilityEl) == 'object') {
594 return this.visibilityEl;
597 if (typeof(this.visibilityEl) == 'string') {
598 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
605 * Show a component - removes 'hidden' class
609 if(!this.getVisibilityEl()){
613 this.getVisibilityEl().removeClass(['hidden','d-none']);
615 this.fireEvent('show', this);
620 * Hide a component - adds 'hidden' class
624 if(!this.getVisibilityEl()){
628 this.getVisibilityEl().addClass(['hidden','d-none']);
630 this.fireEvent('hide', this);
643 * @class Roo.bootstrap.Element
644 * @extends Roo.bootstrap.Component
645 * Bootstrap Element class
646 * @cfg {String} html contents of the element
647 * @cfg {String} tag tag of the element
648 * @cfg {String} cls class of the element
649 * @cfg {Boolean} preventDefault (true|false) default false
650 * @cfg {Boolean} clickable (true|false) default false
653 * Create a new Element
654 * @param {Object} config The config object
657 Roo.bootstrap.Element = function(config){
658 Roo.bootstrap.Element.superclass.constructor.call(this, config);
664 * When a element is chick
665 * @param {Roo.bootstrap.Element} this
666 * @param {Roo.EventObject} e
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
677 preventDefault: false,
680 getAutoCreate : function(){
684 // cls: this.cls, double assign in parent class Component.js :: onRender
691 initEvents: function()
693 Roo.bootstrap.Element.superclass.initEvents.call(this);
696 this.el.on('click', this.onClick, this);
701 onClick : function(e)
703 if(this.preventDefault){
707 this.fireEvent('click', this, e);
710 getValue : function()
712 return this.el.dom.innerHTML;
715 setValue : function(value)
717 this.el.dom.innerHTML = value;
732 * @class Roo.bootstrap.DropTarget
733 * @extends Roo.bootstrap.Element
734 * Bootstrap DropTarget class
736 * @cfg {string} name dropable name
739 * Create a new Dropable Area
740 * @param {Object} config The config object
743 Roo.bootstrap.DropTarget = function(config){
744 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
750 * When a element is chick
751 * @param {Roo.bootstrap.Element} this
752 * @param {Roo.EventObject} e
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
761 getAutoCreate : function(){
766 initEvents: function()
768 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772 drop : this.dragDrop.createDelegate(this),
773 enter : this.dragEnter.createDelegate(this),
774 out : this.dragOut.createDelegate(this),
775 over : this.dragOver.createDelegate(this)
779 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782 dragDrop : function(source,e,data)
784 // user has to decide how to impliment this.
787 //this.fireEvent('drop', this, source, e ,data);
791 dragEnter : function(n, dd, e, data)
793 // probably want to resize the element to match the dropped element..
795 this.originalSize = this.el.getSize();
796 this.el.setSize( n.el.getSize());
797 this.dropZone.DDM.refreshCache(this.name);
798 Roo.log([n, dd, e, data]);
801 dragOut : function(value)
803 // resize back to normal
805 this.el.setSize(this.originalSize);
806 this.dropZone.resetConstraints();
809 dragOver : function()
826 * @class Roo.bootstrap.Body
827 * @extends Roo.bootstrap.Component
828 * Bootstrap Body class
832 * @param {Object} config The config object
835 Roo.bootstrap.Body = function(config){
837 config = config || {};
839 Roo.bootstrap.Body.superclass.constructor.call(this, config);
840 this.el = Roo.get(config.el ? config.el : document.body );
841 if (this.cls && this.cls.length) {
842 Roo.get(document.body).addClass(this.cls);
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
848 is_body : true,// just to make sure it's constructed?
853 onRender : function(ct, position)
855 /* Roo.log("Roo.bootstrap.Body - onRender");
856 if (this.cls && this.cls.length) {
857 Roo.get(document.body).addClass(this.cls);
876 * @class Roo.bootstrap.ButtonGroup
877 * @extends Roo.bootstrap.Component
878 * Bootstrap ButtonGroup class
879 * @cfg {String} size lg | sm | xs (default empty normal)
880 * @cfg {String} align vertical | justified (default none)
881 * @cfg {String} direction up | down (default down)
882 * @cfg {Boolean} toolbar false | true
883 * @cfg {Boolean} btn true | false
888 * @param {Object} config The config object
891 Roo.bootstrap.ButtonGroup = function(config){
892 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
903 getAutoCreate : function(){
909 cfg.html = this.html || cfg.html;
920 if (['vertical','justified'].indexOf(this.align)!==-1) {
921 cfg.cls = 'btn-group-' + this.align;
923 if (this.align == 'justified') {
924 console.log(this.items);
928 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929 cfg.cls += ' btn-group-' + this.size;
932 if (this.direction == 'up') {
933 cfg.cls += ' dropup' ;
939 * Add a button to the group (similar to NavItem API.)
941 addItem : function(cfg)
943 var cn = new Roo.bootstrap.Button(cfg);
945 cn.parentId = this.id;
946 cn.onRender(this.el, null);
960 * @class Roo.bootstrap.Button
961 * @extends Roo.bootstrap.Component
962 * Bootstrap Button class
963 * @cfg {String} html The button content
964 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
965 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
966 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967 * @cfg {String} size ( lg | sm | xs)
968 * @cfg {String} tag ( a | input | submit)
969 * @cfg {String} href empty or href
970 * @cfg {Boolean} disabled default false;
971 * @cfg {Boolean} isClose default false;
972 * @cfg {String} glyphicon depricated - use fa
973 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974 * @cfg {String} badge text for badge
975 * @cfg {String} theme (default|glow)
976 * @cfg {Boolean} inverse dark themed version
977 * @cfg {Boolean} toggle is it a slidy toggle button
978 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
979 * @cfg {String} ontext text for on slidy toggle state
980 * @cfg {String} offtext text for off slidy toggle state
981 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
982 * @cfg {Boolean} removeClass remove the standard class..
983 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
986 * Create a new button
987 * @param {Object} config The config object
991 Roo.bootstrap.Button = function(config){
992 Roo.bootstrap.Button.superclass.constructor.call(this, config);
993 this.weightClass = ["btn-default btn-outline-secondary",
1005 * When a butotn is pressed
1006 * @param {Roo.bootstrap.Button} btn
1007 * @param {Roo.EventObject} e
1012 * After the button has been toggles
1013 * @param {Roo.bootstrap.Button} btn
1014 * @param {Roo.EventObject} e
1015 * @param {boolean} pressed (also available as button.pressed)
1021 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1042 preventDefault: true,
1050 getAutoCreate : function(){
1058 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1059 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1060 this.tag = 'button';
1064 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1066 if (this.toggle == true) {
1069 cls: 'slider-frame roo-button',
1073 'data-on-text':'ON',
1074 'data-off-text':'OFF',
1075 cls: 'slider-button',
1081 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1082 cfg.cls += ' '+this.weight;
1089 cfg.cls += ' close';
1091 cfg["aria-hidden"] = true;
1093 cfg.html = "×";
1099 if (this.theme==='default') {
1100 cfg.cls = 'btn roo-button';
1102 //if (this.parentType != 'Navbar') {
1103 this.weight = this.weight.length ? this.weight : 'default';
1105 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1107 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1108 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1109 cfg.cls += ' btn-' + outline + weight;
1110 if (this.weight == 'default') {
1112 cfg.cls += ' btn-' + this.weight;
1115 } else if (this.theme==='glow') {
1118 cfg.cls = 'btn-glow roo-button';
1120 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1122 cfg.cls += ' ' + this.weight;
1128 this.cls += ' inverse';
1132 if (this.active || this.pressed === true) {
1133 cfg.cls += ' active';
1136 if (this.disabled) {
1137 cfg.disabled = 'disabled';
1141 Roo.log('changing to ul' );
1143 this.glyphicon = 'caret';
1144 if (Roo.bootstrap.version == 4) {
1145 this.fa = 'caret-down';
1150 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1152 //gsRoo.log(this.parentType);
1153 if (this.parentType === 'Navbar' && !this.parent().bar) {
1154 Roo.log('changing to li?');
1163 href : this.href || '#'
1166 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1167 cfg.cls += ' dropdown';
1174 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1176 if (this.glyphicon) {
1177 cfg.html = ' ' + cfg.html;
1182 cls: 'glyphicon glyphicon-' + this.glyphicon
1187 cfg.html = ' ' + cfg.html;
1192 cls: 'fa fas fa-' + this.fa
1202 // cfg.cls='btn roo-button';
1206 var value = cfg.html;
1211 cls: 'glyphicon glyphicon-' + this.glyphicon,
1218 cls: 'fa fas fa-' + this.fa,
1223 var bw = this.badge_weight.length ? this.badge_weight :
1224 (this.weight.length ? this.weight : 'secondary');
1225 bw = bw == 'default' ? 'secondary' : bw;
1231 cls: 'badge badge-' + bw,
1240 cfg.cls += ' dropdown';
1241 cfg.html = typeof(cfg.html) != 'undefined' ?
1242 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1245 if (cfg.tag !== 'a' && this.href !== '') {
1246 throw "Tag must be a to set href.";
1247 } else if (this.href.length > 0) {
1248 cfg.href = this.href;
1251 if(this.removeClass){
1256 cfg.target = this.target;
1261 initEvents: function() {
1262 // Roo.log('init events?');
1263 // Roo.log(this.el.dom);
1266 if (typeof (this.menu) != 'undefined') {
1267 this.menu.parentType = this.xtype;
1268 this.menu.triggerEl = this.el;
1269 this.addxtype(Roo.apply({}, this.menu));
1273 if (this.el.hasClass('roo-button')) {
1274 this.el.on('click', this.onClick, this);
1276 this.el.select('.roo-button').on('click', this.onClick, this);
1279 if(this.removeClass){
1280 this.el.on('click', this.onClick, this);
1283 this.el.enableDisplayMode();
1286 onClick : function(e)
1288 if (this.disabled) {
1292 Roo.log('button on click ');
1293 if(this.preventDefault){
1297 if (this.pressed === true || this.pressed === false) {
1298 this.toggleActive(e);
1302 this.fireEvent('click', this, e);
1306 * Enables this button
1310 this.disabled = false;
1311 this.el.removeClass('disabled');
1315 * Disable this button
1317 disable : function()
1319 this.disabled = true;
1320 this.el.addClass('disabled');
1323 * sets the active state on/off,
1324 * @param {Boolean} state (optional) Force a particular state
1326 setActive : function(v) {
1328 this.el[v ? 'addClass' : 'removeClass']('active');
1332 * toggles the current active state
1334 toggleActive : function(e)
1336 this.setActive(!this.pressed);
1337 this.fireEvent('toggle', this, e, !this.pressed);
1340 * get the current active state
1341 * @return {boolean} true if it's active
1343 isActive : function()
1345 return this.el.hasClass('active');
1348 * set the text of the first selected button
1350 setText : function(str)
1352 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1355 * get the text of the first selected button
1357 getText : function()
1359 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1362 setWeight : function(str)
1364 this.el.removeClass(this.weightClass);
1366 var outline = this.outline ? 'outline-' : '';
1367 if (str == 'default') {
1368 this.el.addClass('btn-default btn-outline-secondary');
1371 this.el.addClass('btn-' + outline + str);
1385 * @class Roo.bootstrap.Column
1386 * @extends Roo.bootstrap.Component
1387 * Bootstrap Column class
1388 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1389 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1390 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1391 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1392 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1393 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1394 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1395 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1398 * @cfg {Boolean} hidden (true|false) hide the element
1399 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1400 * @cfg {String} fa (ban|check|...) font awesome icon
1401 * @cfg {Number} fasize (1|2|....) font awsome size
1403 * @cfg {String} icon (info-sign|check|...) glyphicon name
1405 * @cfg {String} html content of column.
1408 * Create a new Column
1409 * @param {Object} config The config object
1412 Roo.bootstrap.Column = function(config){
1413 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1416 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1434 getAutoCreate : function(){
1435 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1443 var sizes = ['xs','sm','md','lg'];
1444 sizes.map(function(size ,ix){
1445 //Roo.log( size + ':' + settings[size]);
1447 if (settings[size+'off'] !== false) {
1448 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1451 if (settings[size] === false) {
1455 if (!settings[size]) { // 0 = hidden
1456 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1458 for (var i = ix; i > -1; i--) {
1459 cfg.cls += ' d-' + sizes[i] + '-none';
1465 cfg.cls += ' col-' + size + '-' + settings[size] + (
1466 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1472 cfg.cls += ' hidden';
1475 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1476 cfg.cls +=' alert alert-' + this.alert;
1480 if (this.html.length) {
1481 cfg.html = this.html;
1485 if (this.fasize > 1) {
1486 fasize = ' fa-' + this.fasize + 'x';
1488 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1493 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1512 * @class Roo.bootstrap.Container
1513 * @extends Roo.bootstrap.Component
1514 * Bootstrap Container class
1515 * @cfg {Boolean} jumbotron is it a jumbotron element
1516 * @cfg {String} html content of element
1517 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1518 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1519 * @cfg {String} header content of header (for panel)
1520 * @cfg {String} footer content of footer (for panel)
1521 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1522 * @cfg {String} tag (header|aside|section) type of HTML tag.
1523 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1524 * @cfg {String} fa font awesome icon
1525 * @cfg {String} icon (info-sign|check|...) glyphicon name
1526 * @cfg {Boolean} hidden (true|false) hide the element
1527 * @cfg {Boolean} expandable (true|false) default false
1528 * @cfg {Boolean} expanded (true|false) default true
1529 * @cfg {String} rheader contet on the right of header
1530 * @cfg {Boolean} clickable (true|false) default false
1534 * Create a new Container
1535 * @param {Object} config The config object
1538 Roo.bootstrap.Container = function(config){
1539 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1545 * After the panel has been expand
1547 * @param {Roo.bootstrap.Container} this
1552 * After the panel has been collapsed
1554 * @param {Roo.bootstrap.Container} this
1559 * When a element is chick
1560 * @param {Roo.bootstrap.Container} this
1561 * @param {Roo.EventObject} e
1567 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1585 getChildContainer : function() {
1591 if (this.panel.length) {
1592 return this.el.select('.panel-body',true).first();
1599 getAutoCreate : function(){
1602 tag : this.tag || 'div',
1606 if (this.jumbotron) {
1607 cfg.cls = 'jumbotron';
1612 // - this is applied by the parent..
1614 // cfg.cls = this.cls + '';
1617 if (this.sticky.length) {
1619 var bd = Roo.get(document.body);
1620 if (!bd.hasClass('bootstrap-sticky')) {
1621 bd.addClass('bootstrap-sticky');
1622 Roo.select('html',true).setStyle('height', '100%');
1625 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1629 if (this.well.length) {
1630 switch (this.well) {
1633 cfg.cls +=' well well-' +this.well;
1642 cfg.cls += ' hidden';
1646 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1647 cfg.cls +=' alert alert-' + this.alert;
1652 if (this.panel.length) {
1653 cfg.cls += ' panel panel-' + this.panel;
1655 if (this.header.length) {
1659 if(this.expandable){
1661 cfg.cls = cfg.cls + ' expandable';
1665 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1673 cls : 'panel-title',
1674 html : (this.expandable ? ' ' : '') + this.header
1678 cls: 'panel-header-right',
1684 cls : 'panel-heading',
1685 style : this.expandable ? 'cursor: pointer' : '',
1693 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1698 if (this.footer.length) {
1700 cls : 'panel-footer',
1709 body.html = this.html || cfg.html;
1710 // prefix with the icons..
1712 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1715 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1720 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1721 cfg.cls = 'container';
1727 initEvents: function()
1729 if(this.expandable){
1730 var headerEl = this.headerEl();
1733 headerEl.on('click', this.onToggleClick, this);
1738 this.el.on('click', this.onClick, this);
1743 onToggleClick : function()
1745 var headerEl = this.headerEl();
1761 if(this.fireEvent('expand', this)) {
1763 this.expanded = true;
1765 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1767 this.el.select('.panel-body',true).first().removeClass('hide');
1769 var toggleEl = this.toggleEl();
1775 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1780 collapse : function()
1782 if(this.fireEvent('collapse', this)) {
1784 this.expanded = false;
1786 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1787 this.el.select('.panel-body',true).first().addClass('hide');
1789 var toggleEl = this.toggleEl();
1795 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1799 toggleEl : function()
1801 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1805 return this.el.select('.panel-heading .fa',true).first();
1808 headerEl : function()
1810 if(!this.el || !this.panel.length || !this.header.length){
1814 return this.el.select('.panel-heading',true).first()
1819 if(!this.el || !this.panel.length){
1823 return this.el.select('.panel-body',true).first()
1826 titleEl : function()
1828 if(!this.el || !this.panel.length || !this.header.length){
1832 return this.el.select('.panel-title',true).first();
1835 setTitle : function(v)
1837 var titleEl = this.titleEl();
1843 titleEl.dom.innerHTML = v;
1846 getTitle : function()
1849 var titleEl = this.titleEl();
1855 return titleEl.dom.innerHTML;
1858 setRightTitle : function(v)
1860 var t = this.el.select('.panel-header-right',true).first();
1866 t.dom.innerHTML = v;
1869 onClick : function(e)
1873 this.fireEvent('click', this, e);
1880 * This is BS4's Card element.. - similar to our containers probably..
1884 * @class Roo.bootstrap.Card
1885 * @extends Roo.bootstrap.Component
1886 * Bootstrap Card class
1889 * possible... may not be implemented..
1890 * @cfg {String} header_image src url of image.
1891 * @cfg {String|Object} header
1892 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1894 * @cfg {String} title
1895 * @cfg {String} subtitle
1896 * @cfg {String} html -- html contents - or just use children..
1897 * @cfg {String} footer
1899 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1901 * @cfg {String} margin (0|1|2|3|4|5|auto)
1902 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1903 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1904 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1905 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1906 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1907 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1909 * @cfg {String} padding (0|1|2|3|4|5)
1910 * @cfg {String} padding_top (0|1|2|3|4|5)
1911 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1912 * @cfg {String} padding_left (0|1|2|3|4|5)
1913 * @cfg {String} padding_right (0|1|2|3|4|5)
1914 * @cfg {String} padding_x (0|1|2|3|4|5)
1915 * @cfg {String} padding_y (0|1|2|3|4|5)
1917 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1918 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1919 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1920 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1921 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1923 * @config {Boolean} dragable if this card can be dragged.
1924 * @config {String} drag_group group for drag
1925 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1926 * @config {String} drop_group group for drag
1928 * @config {Boolean} collapsable can the body be collapsed.
1929 * @config {Boolean} collapsed is the body collapsed when rendered...
1931 * Create a new Container
1932 * @param {Object} config The config object
1935 Roo.bootstrap.Card = function(config){
1936 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1944 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1949 margin: '', /// may be better in component?
1979 collapsable : false,
1986 childContainer : false,
1988 layoutCls : function()
1992 Roo.log(this.margin_bottom.length);
1993 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1994 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1996 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1997 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1999 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2000 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2004 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2005 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2006 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2010 // more generic support?
2018 // Roo.log("Call onRender: " + this.xtype);
2019 /* We are looking at something like this.
2021 <img src="..." class="card-img-top" alt="...">
2022 <div class="card-body">
2023 <h5 class="card-title">Card title</h5>
2024 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2026 >> this bit is really the body...
2027 <div> << we will ad dthis in hopefully it will not break shit.
2029 ** card text does not actually have any styling...
2031 <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>
2034 <a href="#" class="card-link">Card link</a>
2037 <div class="card-footer">
2038 <small class="text-muted">Last updated 3 mins ago</small>
2042 getAutoCreate : function(){
2050 if (this.weight.length && this.weight != 'light') {
2051 cfg.cls += ' text-white';
2053 cfg.cls += ' text-dark'; // need as it's nested..
2055 if (this.weight.length) {
2056 cfg.cls += ' bg-' + this.weight;
2059 cfg.cls += this.layoutCls();
2062 if (this.header.length) {
2064 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2065 cls : 'card-header',
2073 cls : 'card-header d-none',
2078 if (this.collapsable) {
2081 cls : 'd-block user-select-none',
2085 cls : 'roo-collapse-toggle fa fa-chevron-down float-right'
2090 hdr.cn.push(hdr_ctr);
2092 if (this.header.length) {
2095 cls: 'roo-card-header-ctr',
2100 if (this.header_image.length) {
2103 cls : 'card-img-top',
2104 src: this.header_image // escape?
2109 cls : 'card-img-top d-none'
2119 if (this.collapsable) {
2122 cls : 'roo-collapsable collapse ' + (this.collapsed ? '' : 'show'),
2129 if (this.title.length) {
2133 src: this.title // escape?
2137 if (this.subtitle.length) {
2141 src: this.subtitle // escape?
2147 cls : 'roo-card-body-ctr'
2150 if (this.html.length) {
2156 // fixme ? handle objects?
2157 if (this.footer.length) {
2160 cls : 'card-footer',
2161 html: this.footer // escape?
2170 getCardHeader : function()
2172 var ret = this.el.select('.card-header',true).first();
2173 if (ret.hasClass('d-none')) {
2174 ret.removeClass('d-none');
2180 getCardImageTop : function()
2182 var ret = this.el.select('.card-img-top',true).first();
2183 if (ret.hasClass('d-none')) {
2184 ret.removeClass('d-none');
2190 getChildContainer : function()
2196 return this.el.select('.roo-card-body-ctr',true).first();
2199 initEvents: function()
2202 this.bodyEl = this.getChildContainer();
2204 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2205 containerScroll: true,
2206 ddGroup: this.drag_group || 'default_card_drag_group'
2208 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2210 if (this.dropable) {
2211 this.dropZone = new Roo.dd.DropZone(this.getChildContainer(), {
2212 containerScroll: true,
2213 ddGroup: this.drop_group || 'default_card_drag_group'
2215 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2216 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2217 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2218 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2219 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2222 if (this.collapsable) {
2223 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2226 getDragData : function(e) {
2227 var target = this.getEl();
2229 //this.handleSelection(e);
2234 nodes: this.getEl(),
2239 dragData.ddel = target.dom ; // the div element
2240 Roo.log(target.getWidth( ));
2241 dragData.ddel.style.width = target.getWidth() + 'px';
2248 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2249 * whole Element becomes the target, and this causes the drop gesture to append.
2251 getTargetFromEvent : function(e)
2253 var target = e.getTarget();
2254 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2255 target = target.parentNode;
2257 // see if target is one of the 'cards'...
2258 var ctarget = false;
2260 for (var i = 0;i< this.items.length;i++) {
2261 if (this.items[i].xtype != 'Card') {
2264 cards.push(this.items[i].el.dom);
2265 if (target == this.items[i].el.dom) {
2271 ctarget = cards[cards.length-1] || this.el.dom;
2275 Roo.log(['getTargetFromEvent', ctarget]);
2279 onNodeEnter : function(n, dd, e, data){
2282 onNodeOver : function(n, dd, e, data)
2284 var pt = this.getDropPoint(e, n, dd);
2285 // set the insert point style on the target node
2286 //var dragElClass = this.dropNotAllowed;
2290 return false; //dragElClass;
2292 onNodeOut : function(n, dd, e, data){
2293 //this.removeDropIndicators(n);
2295 onNodeDrop : function(n, dd, e, data)
2299 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2302 var pt = this.getDropPoint(e, n, dd);
2303 var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2304 if (pt == "below") {
2307 for (var i = 0; i < this.items.length; i++) {
2308 var r = this.items[i];
2309 //var dup = this.store.getById(r.id);
2310 if (dup && (dd != this.dragZone)) {
2311 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2314 this.store.insert(insertAt++, r.copy());
2316 data.source.isDirtyFlag = true;
2318 this.store.insert(insertAt++, r);
2320 this.isDirtyFlag = true;
2323 this.dragZone.cachedTarget = null;
2327 /** Decide whether to drop above or below a View node. */
2328 getDropPoint : function(e, n, dd)
2330 if (n == this.bodyEl.dom) {
2333 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2334 var c = t + (b - t) / 2;
2335 var y = Roo.lib.Event.getPageY(e);
2342 onToggleCollapse : function(e)
2344 if (this.collapsed) {
2345 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2346 this.el.select('.roo-collapsable').addClass('show');
2347 this.collapsed = false;
2350 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2351 this.el.select('.roo-collapsable').removeClass('show');
2352 this.collapsed = true;
2363 * Card header - holder for the card header elements.
2368 * @class Roo.bootstrap.CardHeader
2369 * @extends Roo.bootstrap.Element
2370 * Bootstrap CardHeader class
2372 * Create a new Card Header - that you can embed children into
2373 * @param {Object} config The config object
2376 Roo.bootstrap.CardHeader = function(config){
2377 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2380 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2383 container_method : 'getCardHeader'
2396 * Card header - holder for the card header elements.
2401 * @class Roo.bootstrap.CardImageTop
2402 * @extends Roo.bootstrap.Element
2403 * Bootstrap CardImageTop class
2405 * Create a new Card Image Top container
2406 * @param {Object} config The config object
2409 Roo.bootstrap.CardImageTop = function(config){
2410 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2413 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2416 container_method : 'getCardImageTop'
2434 * @class Roo.bootstrap.Img
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap Img class
2437 * @cfg {Boolean} imgResponsive false | true
2438 * @cfg {String} border rounded | circle | thumbnail
2439 * @cfg {String} src image source
2440 * @cfg {String} alt image alternative text
2441 * @cfg {String} href a tag href
2442 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2443 * @cfg {String} xsUrl xs image source
2444 * @cfg {String} smUrl sm image source
2445 * @cfg {String} mdUrl md image source
2446 * @cfg {String} lgUrl lg image source
2449 * Create a new Input
2450 * @param {Object} config The config object
2453 Roo.bootstrap.Img = function(config){
2454 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2460 * The img click event for the img.
2461 * @param {Roo.EventObject} e
2467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2469 imgResponsive: true,
2479 getAutoCreate : function()
2481 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2482 return this.createSingleImg();
2487 cls: 'roo-image-responsive-group',
2492 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2494 if(!_this[size + 'Url']){
2500 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2501 html: _this.html || cfg.html,
2502 src: _this[size + 'Url']
2505 img.cls += ' roo-image-responsive-' + size;
2507 var s = ['xs', 'sm', 'md', 'lg'];
2509 s.splice(s.indexOf(size), 1);
2511 Roo.each(s, function(ss){
2512 img.cls += ' hidden-' + ss;
2515 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2516 cfg.cls += ' img-' + _this.border;
2520 cfg.alt = _this.alt;
2533 a.target = _this.target;
2537 cfg.cn.push((_this.href) ? a : img);
2544 createSingleImg : function()
2548 cls: (this.imgResponsive) ? 'img-responsive' : '',
2550 src : 'about:blank' // just incase src get's set to undefined?!?
2553 cfg.html = this.html || cfg.html;
2555 cfg.src = this.src || cfg.src;
2557 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2558 cfg.cls += ' img-' + this.border;
2575 a.target = this.target;
2580 return (this.href) ? a : cfg;
2583 initEvents: function()
2586 this.el.on('click', this.onClick, this);
2591 onClick : function(e)
2593 Roo.log('img onclick');
2594 this.fireEvent('click', this, e);
2597 * Sets the url of the image - used to update it
2598 * @param {String} url the url of the image
2601 setSrc : function(url)
2605 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2606 this.el.dom.src = url;
2610 this.el.select('img', true).first().dom.src = url;
2626 * @class Roo.bootstrap.Link
2627 * @extends Roo.bootstrap.Component
2628 * Bootstrap Link Class
2629 * @cfg {String} alt image alternative text
2630 * @cfg {String} href a tag href
2631 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2632 * @cfg {String} html the content of the link.
2633 * @cfg {String} anchor name for the anchor link
2634 * @cfg {String} fa - favicon
2636 * @cfg {Boolean} preventDefault (true | false) default false
2640 * Create a new Input
2641 * @param {Object} config The config object
2644 Roo.bootstrap.Link = function(config){
2645 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2651 * The img click event for the img.
2652 * @param {Roo.EventObject} e
2658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2662 preventDefault: false,
2668 getAutoCreate : function()
2670 var html = this.html || '';
2672 if (this.fa !== false) {
2673 html = '<i class="fa fa-' + this.fa + '"></i>';
2678 // anchor's do not require html/href...
2679 if (this.anchor === false) {
2681 cfg.href = this.href || '#';
2683 cfg.name = this.anchor;
2684 if (this.html !== false || this.fa !== false) {
2687 if (this.href !== false) {
2688 cfg.href = this.href;
2692 if(this.alt !== false){
2697 if(this.target !== false) {
2698 cfg.target = this.target;
2704 initEvents: function() {
2706 if(!this.href || this.preventDefault){
2707 this.el.on('click', this.onClick, this);
2711 onClick : function(e)
2713 if(this.preventDefault){
2716 //Roo.log('img onclick');
2717 this.fireEvent('click', this, e);
2730 * @class Roo.bootstrap.Header
2731 * @extends Roo.bootstrap.Component
2732 * Bootstrap Header class
2733 * @cfg {String} html content of header
2734 * @cfg {Number} level (1|2|3|4|5|6) default 1
2737 * Create a new Header
2738 * @param {Object} config The config object
2742 Roo.bootstrap.Header = function(config){
2743 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2754 getAutoCreate : function(){
2759 tag: 'h' + (1 *this.level),
2760 html: this.html || ''
2772 * Ext JS Library 1.1.1
2773 * Copyright(c) 2006-2007, Ext JS, LLC.
2775 * Originally Released Under LGPL - original licence link has changed is not relivant.
2778 * <script type="text/javascript">
2782 * @class Roo.bootstrap.MenuMgr
2783 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2786 Roo.bootstrap.MenuMgr = function(){
2787 var menus, active, groups = {}, attached = false, lastShow = new Date();
2789 // private - called when first menu is created
2792 active = new Roo.util.MixedCollection();
2793 Roo.get(document).addKeyListener(27, function(){
2794 if(active.length > 0){
2802 if(active && active.length > 0){
2803 var c = active.clone();
2813 if(active.length < 1){
2814 Roo.get(document).un("mouseup", onMouseDown);
2822 var last = active.last();
2823 lastShow = new Date();
2826 Roo.get(document).on("mouseup", onMouseDown);
2831 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2832 m.parentMenu.activeChild = m;
2833 }else if(last && last.isVisible()){
2834 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2839 function onBeforeHide(m){
2841 m.activeChild.hide();
2843 if(m.autoHideTimer){
2844 clearTimeout(m.autoHideTimer);
2845 delete m.autoHideTimer;
2850 function onBeforeShow(m){
2851 var pm = m.parentMenu;
2852 if(!pm && !m.allowOtherMenus){
2854 }else if(pm && pm.activeChild && active != m){
2855 pm.activeChild.hide();
2859 // private this should really trigger on mouseup..
2860 function onMouseDown(e){
2861 Roo.log("on Mouse Up");
2863 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2864 Roo.log("MenuManager hideAll");
2873 function onBeforeCheck(mi, state){
2875 var g = groups[mi.group];
2876 for(var i = 0, l = g.length; i < l; i++){
2878 g[i].setChecked(false);
2887 * Hides all menus that are currently visible
2889 hideAll : function(){
2894 register : function(menu){
2898 menus[menu.id] = menu;
2899 menu.on("beforehide", onBeforeHide);
2900 menu.on("hide", onHide);
2901 menu.on("beforeshow", onBeforeShow);
2902 menu.on("show", onShow);
2904 if(g && menu.events["checkchange"]){
2908 groups[g].push(menu);
2909 menu.on("checkchange", onCheck);
2914 * Returns a {@link Roo.menu.Menu} object
2915 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2916 * be used to generate and return a new Menu instance.
2918 get : function(menu){
2919 if(typeof menu == "string"){ // menu id
2921 }else if(menu.events){ // menu instance
2924 /*else if(typeof menu.length == 'number'){ // array of menu items?
2925 return new Roo.bootstrap.Menu({items:menu});
2926 }else{ // otherwise, must be a config
2927 return new Roo.bootstrap.Menu(menu);
2934 unregister : function(menu){
2935 delete menus[menu.id];
2936 menu.un("beforehide", onBeforeHide);
2937 menu.un("hide", onHide);
2938 menu.un("beforeshow", onBeforeShow);
2939 menu.un("show", onShow);
2941 if(g && menu.events["checkchange"]){
2942 groups[g].remove(menu);
2943 menu.un("checkchange", onCheck);
2948 registerCheckable : function(menuItem){
2949 var g = menuItem.group;
2954 groups[g].push(menuItem);
2955 menuItem.on("beforecheckchange", onBeforeCheck);
2960 unregisterCheckable : function(menuItem){
2961 var g = menuItem.group;
2963 groups[g].remove(menuItem);
2964 menuItem.un("beforecheckchange", onBeforeCheck);
2976 * @class Roo.bootstrap.Menu
2977 * @extends Roo.bootstrap.Component
2978 * Bootstrap Menu class - container for MenuItems
2979 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2980 * @cfg {bool} hidden if the menu should be hidden when rendered.
2981 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2982 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2986 * @param {Object} config The config object
2990 Roo.bootstrap.Menu = function(config){
2991 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2992 if (this.registerMenu && this.type != 'treeview') {
2993 Roo.bootstrap.MenuMgr.register(this);
3000 * Fires before this menu is displayed (return false to block)
3001 * @param {Roo.menu.Menu} this
3006 * Fires before this menu is hidden (return false to block)
3007 * @param {Roo.menu.Menu} this
3012 * Fires after this menu is displayed
3013 * @param {Roo.menu.Menu} this
3018 * Fires after this menu is hidden
3019 * @param {Roo.menu.Menu} this
3024 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3025 * @param {Roo.menu.Menu} this
3026 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3027 * @param {Roo.EventObject} e
3032 * Fires when the mouse is hovering over this menu
3033 * @param {Roo.menu.Menu} this
3034 * @param {Roo.EventObject} e
3035 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3040 * Fires when the mouse exits this menu
3041 * @param {Roo.menu.Menu} this
3042 * @param {Roo.EventObject} e
3043 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3048 * Fires when a menu item contained in this menu is clicked
3049 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3050 * @param {Roo.EventObject} e
3054 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3057 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3061 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3064 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3066 registerMenu : true,
3068 menuItems :false, // stores the menu items..
3078 getChildContainer : function() {
3082 getAutoCreate : function(){
3084 //if (['right'].indexOf(this.align)!==-1) {
3085 // cfg.cn[1].cls += ' pull-right'
3091 cls : 'dropdown-menu' ,
3092 style : 'z-index:1000'
3096 if (this.type === 'submenu') {
3097 cfg.cls = 'submenu active';
3099 if (this.type === 'treeview') {
3100 cfg.cls = 'treeview-menu';
3105 initEvents : function() {
3107 // Roo.log("ADD event");
3108 // Roo.log(this.triggerEl.dom);
3110 this.triggerEl.on('click', this.onTriggerClick, this);
3112 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3115 if (this.triggerEl.hasClass('nav-item')) {
3116 // dropdown toggle on the 'a' in BS4?
3117 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3119 this.triggerEl.addClass('dropdown-toggle');
3122 this.el.on('touchstart' , this.onTouch, this);
3124 this.el.on('click' , this.onClick, this);
3126 this.el.on("mouseover", this.onMouseOver, this);
3127 this.el.on("mouseout", this.onMouseOut, this);
3131 findTargetItem : function(e)
3133 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3137 //Roo.log(t); Roo.log(t.id);
3139 //Roo.log(this.menuitems);
3140 return this.menuitems.get(t.id);
3142 //return this.items.get(t.menuItemId);
3148 onTouch : function(e)
3150 Roo.log("menu.onTouch");
3151 //e.stopEvent(); this make the user popdown broken
3155 onClick : function(e)
3157 Roo.log("menu.onClick");
3159 var t = this.findTargetItem(e);
3160 if(!t || t.isContainer){
3165 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3166 if(t == this.activeItem && t.shouldDeactivate(e)){
3167 this.activeItem.deactivate();
3168 delete this.activeItem;
3172 this.setActiveItem(t, true);
3180 Roo.log('pass click event');
3184 this.fireEvent("click", this, t, e);
3188 if(!t.href.length || t.href == '#'){
3189 (function() { _this.hide(); }).defer(100);
3194 onMouseOver : function(e){
3195 var t = this.findTargetItem(e);
3198 // if(t.canActivate && !t.disabled){
3199 // this.setActiveItem(t, true);
3203 this.fireEvent("mouseover", this, e, t);
3205 isVisible : function(){
3206 return !this.hidden;
3208 onMouseOut : function(e){
3209 var t = this.findTargetItem(e);
3212 // if(t == this.activeItem && t.shouldDeactivate(e)){
3213 // this.activeItem.deactivate();
3214 // delete this.activeItem;
3217 this.fireEvent("mouseout", this, e, t);
3222 * Displays this menu relative to another element
3223 * @param {String/HTMLElement/Roo.Element} element The element to align to
3224 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3225 * the element (defaults to this.defaultAlign)
3226 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3228 show : function(el, pos, parentMenu)
3230 if (false === this.fireEvent("beforeshow", this)) {
3231 Roo.log("show canceled");
3234 this.parentMenu = parentMenu;
3239 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3242 * Displays this menu at a specific xy position
3243 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3244 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3246 showAt : function(xy, parentMenu, /* private: */_e){
3247 this.parentMenu = parentMenu;
3252 this.fireEvent("beforeshow", this);
3253 //xy = this.el.adjustForConstraints(xy);
3257 this.hideMenuItems();
3258 this.hidden = false;
3259 this.triggerEl.addClass('open');
3260 this.el.addClass('show');
3262 // reassign x when hitting right
3263 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3264 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3267 // reassign y when hitting bottom
3268 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3269 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3272 // but the list may align on trigger left or trigger top... should it be a properity?
3274 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3279 this.fireEvent("show", this);
3285 this.doFocus.defer(50, this);
3289 doFocus : function(){
3291 this.focusEl.focus();
3296 * Hides this menu and optionally all parent menus
3297 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3299 hide : function(deep)
3301 if (false === this.fireEvent("beforehide", this)) {
3302 Roo.log("hide canceled");
3305 this.hideMenuItems();
3306 if(this.el && this.isVisible()){
3308 if(this.activeItem){
3309 this.activeItem.deactivate();
3310 this.activeItem = null;
3312 this.triggerEl.removeClass('open');;
3313 this.el.removeClass('show');
3315 this.fireEvent("hide", this);
3317 if(deep === true && this.parentMenu){
3318 this.parentMenu.hide(true);
3322 onTriggerClick : function(e)
3324 Roo.log('trigger click');
3326 var target = e.getTarget();
3328 Roo.log(target.nodeName.toLowerCase());
3330 if(target.nodeName.toLowerCase() === 'i'){
3336 onTriggerPress : function(e)
3338 Roo.log('trigger press');
3339 //Roo.log(e.getTarget());
3340 // Roo.log(this.triggerEl.dom);
3342 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3343 var pel = Roo.get(e.getTarget());
3344 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3345 Roo.log('is treeview or dropdown?');
3349 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3353 if (this.isVisible()) {
3358 this.show(this.triggerEl, '?', false);
3361 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3368 hideMenuItems : function()
3370 Roo.log("hide Menu Items");
3375 this.el.select('.open',true).each(function(aa) {
3377 aa.removeClass('open');
3381 addxtypeChild : function (tree, cntr) {
3382 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3384 this.menuitems.add(comp);
3396 this.getEl().dom.innerHTML = '';
3397 this.menuitems.clear();
3411 * @class Roo.bootstrap.MenuItem
3412 * @extends Roo.bootstrap.Component
3413 * Bootstrap MenuItem class
3414 * @cfg {String} html the menu label
3415 * @cfg {String} href the link
3416 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3417 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3418 * @cfg {Boolean} active used on sidebars to highlight active itesm
3419 * @cfg {String} fa favicon to show on left of menu item.
3420 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3424 * Create a new MenuItem
3425 * @param {Object} config The config object
3429 Roo.bootstrap.MenuItem = function(config){
3430 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3435 * The raw click event for the entire grid.
3436 * @param {Roo.bootstrap.MenuItem} this
3437 * @param {Roo.EventObject} e
3443 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3447 preventDefault: false,
3448 isContainer : false,
3452 getAutoCreate : function(){
3454 if(this.isContainer){
3457 cls: 'dropdown-menu-item '
3467 cls : 'dropdown-item',
3472 if (this.fa !== false) {
3475 cls : 'fa fa-' + this.fa
3484 cls: 'dropdown-menu-item',
3487 if (this.parent().type == 'treeview') {
3488 cfg.cls = 'treeview-menu';
3491 cfg.cls += ' active';
3496 anc.href = this.href || cfg.cn[0].href ;
3497 ctag.html = this.html || cfg.cn[0].html ;
3501 initEvents: function()
3503 if (this.parent().type == 'treeview') {
3504 this.el.select('a').on('click', this.onClick, this);
3508 this.menu.parentType = this.xtype;
3509 this.menu.triggerEl = this.el;
3510 this.menu = this.addxtype(Roo.apply({}, this.menu));
3514 onClick : function(e)
3516 Roo.log('item on click ');
3518 if(this.preventDefault){
3521 //this.parent().hideMenuItems();
3523 this.fireEvent('click', this, e);
3542 * @class Roo.bootstrap.MenuSeparator
3543 * @extends Roo.bootstrap.Component
3544 * Bootstrap MenuSeparator class
3547 * Create a new MenuItem
3548 * @param {Object} config The config object
3552 Roo.bootstrap.MenuSeparator = function(config){
3553 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3556 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3558 getAutoCreate : function(){
3577 * @class Roo.bootstrap.Modal
3578 * @extends Roo.bootstrap.Component
3579 * Bootstrap Modal class
3580 * @cfg {String} title Title of dialog
3581 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3582 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3583 * @cfg {Boolean} specificTitle default false
3584 * @cfg {Array} buttons Array of buttons or standard button set..
3585 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3586 * @cfg {Boolean} animate default true
3587 * @cfg {Boolean} allow_close default true
3588 * @cfg {Boolean} fitwindow default false
3589 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3590 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3591 * @cfg {String} size (sm|lg) default empty
3592 * @cfg {Number} max_width set the max width of modal
3596 * Create a new Modal Dialog
3597 * @param {Object} config The config object
3600 Roo.bootstrap.Modal = function(config){
3601 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3606 * The raw btnclick event for the button
3607 * @param {Roo.EventObject} e
3612 * Fire when dialog resize
3613 * @param {Roo.bootstrap.Modal} this
3614 * @param {Roo.EventObject} e
3618 this.buttons = this.buttons || [];
3621 this.tmpl = Roo.factory(this.tmpl);
3626 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3628 title : 'test dialog',
3638 specificTitle: false,
3640 buttonPosition: 'right',
3663 onRender : function(ct, position)
3665 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3668 var cfg = Roo.apply({}, this.getAutoCreate());
3671 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3673 //if (!cfg.name.length) {
3677 cfg.cls += ' ' + this.cls;
3680 cfg.style = this.style;
3682 this.el = Roo.get(document.body).createChild(cfg, position);
3684 //var type = this.el.dom.type;
3687 if(this.tabIndex !== undefined){
3688 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3691 this.dialogEl = this.el.select('.modal-dialog',true).first();
3692 this.bodyEl = this.el.select('.modal-body',true).first();
3693 this.closeEl = this.el.select('.modal-header .close', true).first();
3694 this.headerEl = this.el.select('.modal-header',true).first();
3695 this.titleEl = this.el.select('.modal-title',true).first();
3696 this.footerEl = this.el.select('.modal-footer',true).first();
3698 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3700 //this.el.addClass("x-dlg-modal");
3702 if (this.buttons.length) {
3703 Roo.each(this.buttons, function(bb) {
3704 var b = Roo.apply({}, bb);
3705 b.xns = b.xns || Roo.bootstrap;
3706 b.xtype = b.xtype || 'Button';
3707 if (typeof(b.listeners) == 'undefined') {
3708 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3711 var btn = Roo.factory(b);
3713 btn.render(this.getButtonContainer());
3717 // render the children.
3720 if(typeof(this.items) != 'undefined'){
3721 var items = this.items;
3724 for(var i =0;i < items.length;i++) {
3725 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3729 this.items = nitems;
3731 // where are these used - they used to be body/close/footer
3735 //this.el.addClass([this.fieldClass, this.cls]);
3739 getAutoCreate : function()
3741 // we will default to modal-body-overflow - might need to remove or make optional later.
3743 cls : 'modal-body enable-modal-body-overflow ',
3744 html : this.html || ''
3749 cls : 'modal-title',
3753 if(this.specificTitle){
3759 if (this.allow_close && Roo.bootstrap.version == 3) {
3769 if (this.allow_close && Roo.bootstrap.version == 4) {
3779 if(this.size.length){
3780 size = 'modal-' + this.size;
3783 var footer = Roo.bootstrap.version == 3 ?
3785 cls : 'modal-footer',
3789 cls: 'btn-' + this.buttonPosition
3794 { // BS4 uses mr-auto on left buttons....
3795 cls : 'modal-footer'
3806 cls: "modal-dialog " + size,
3809 cls : "modal-content",
3812 cls : 'modal-header',
3827 modal.cls += ' fade';
3833 getChildContainer : function() {
3838 getButtonContainer : function() {
3840 return Roo.bootstrap.version == 4 ?
3841 this.el.select('.modal-footer',true).first()
3842 : this.el.select('.modal-footer div',true).first();
3845 initEvents : function()
3847 if (this.allow_close) {
3848 this.closeEl.on('click', this.hide, this);
3850 Roo.EventManager.onWindowResize(this.resize, this, true);
3858 this.maskEl.setSize(
3859 Roo.lib.Dom.getViewWidth(true),
3860 Roo.lib.Dom.getViewHeight(true)
3863 if (this.fitwindow) {
3867 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3868 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3873 if(this.max_width !== 0) {
3875 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3878 this.setSize(w, this.height);
3882 if(this.max_height) {
3883 this.setSize(w,Math.min(
3885 Roo.lib.Dom.getViewportHeight(true) - 60
3891 if(!this.fit_content) {
3892 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3896 this.setSize(w, Math.min(
3898 this.headerEl.getHeight() +
3899 this.footerEl.getHeight() +
3900 this.getChildHeight(this.bodyEl.dom.childNodes),
3901 Roo.lib.Dom.getViewportHeight(true) - 60)
3907 setSize : function(w,h)
3918 if (!this.rendered) {
3922 //this.el.setStyle('display', 'block');
3923 this.el.removeClass('hideing');
3924 this.el.dom.style.display='block';
3926 Roo.get(document.body).addClass('modal-open');
3928 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3931 this.el.addClass('show');
3932 this.el.addClass('in');
3935 this.el.addClass('show');
3936 this.el.addClass('in');
3939 // not sure how we can show data in here..
3941 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3944 Roo.get(document.body).addClass("x-body-masked");
3946 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3947 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3948 this.maskEl.dom.style.display = 'block';
3949 this.maskEl.addClass('show');
3954 this.fireEvent('show', this);
3956 // set zindex here - otherwise it appears to be ignored...
3957 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3960 this.items.forEach( function(e) {
3961 e.layout ? e.layout() : false;
3969 if(this.fireEvent("beforehide", this) !== false){
3971 this.maskEl.removeClass('show');
3973 this.maskEl.dom.style.display = '';
3974 Roo.get(document.body).removeClass("x-body-masked");
3975 this.el.removeClass('in');
3976 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3978 if(this.animate){ // why
3979 this.el.addClass('hideing');
3980 this.el.removeClass('show');
3982 if (!this.el.hasClass('hideing')) {
3983 return; // it's been shown again...
3986 this.el.dom.style.display='';
3988 Roo.get(document.body).removeClass('modal-open');
3989 this.el.removeClass('hideing');
3993 this.el.removeClass('show');
3994 this.el.dom.style.display='';
3995 Roo.get(document.body).removeClass('modal-open');
3998 this.fireEvent('hide', this);
4001 isVisible : function()
4004 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4008 addButton : function(str, cb)
4012 var b = Roo.apply({}, { html : str } );
4013 b.xns = b.xns || Roo.bootstrap;
4014 b.xtype = b.xtype || 'Button';
4015 if (typeof(b.listeners) == 'undefined') {
4016 b.listeners = { click : cb.createDelegate(this) };
4019 var btn = Roo.factory(b);
4021 btn.render(this.getButtonContainer());
4027 setDefaultButton : function(btn)
4029 //this.el.select('.modal-footer').()
4032 resizeTo: function(w,h)
4034 this.dialogEl.setWidth(w);
4036 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4038 this.bodyEl.setHeight(h - diff);
4040 this.fireEvent('resize', this);
4043 setContentSize : function(w, h)
4047 onButtonClick: function(btn,e)
4050 this.fireEvent('btnclick', btn.name, e);
4053 * Set the title of the Dialog
4054 * @param {String} str new Title
4056 setTitle: function(str) {
4057 this.titleEl.dom.innerHTML = str;
4060 * Set the body of the Dialog
4061 * @param {String} str new Title
4063 setBody: function(str) {
4064 this.bodyEl.dom.innerHTML = str;
4067 * Set the body of the Dialog using the template
4068 * @param {Obj} data - apply this data to the template and replace the body contents.
4070 applyBody: function(obj)
4073 Roo.log("Error - using apply Body without a template");
4076 this.tmpl.overwrite(this.bodyEl, obj);
4079 getChildHeight : function(child_nodes)
4083 child_nodes.length == 0
4088 var child_height = 0;
4090 for(var i = 0; i < child_nodes.length; i++) {
4093 * for modal with tabs...
4094 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4096 var layout_childs = child_nodes[i].childNodes;
4098 for(var j = 0; j < layout_childs.length; j++) {
4100 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4102 var layout_body_childs = layout_childs[j].childNodes;
4104 for(var k = 0; k < layout_body_childs.length; k++) {
4106 if(layout_body_childs[k].classList.contains('navbar')) {
4107 child_height += layout_body_childs[k].offsetHeight;
4111 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4113 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4115 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4117 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4118 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4133 child_height += child_nodes[i].offsetHeight;
4134 // Roo.log(child_nodes[i].offsetHeight);
4137 return child_height;
4143 Roo.apply(Roo.bootstrap.Modal, {
4145 * Button config that displays a single OK button
4154 * Button config that displays Yes and No buttons
4170 * Button config that displays OK and Cancel buttons
4185 * Button config that displays Yes, No and Cancel buttons
4209 * messagebox - can be used as a replace
4213 * @class Roo.MessageBox
4214 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4218 Roo.Msg.alert('Status', 'Changes saved successfully.');
4220 // Prompt for user data:
4221 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4223 // process text value...
4227 // Show a dialog using config options:
4229 title:'Save Changes?',
4230 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4231 buttons: Roo.Msg.YESNOCANCEL,
4238 Roo.bootstrap.MessageBox = function(){
4239 var dlg, opt, mask, waitTimer;
4240 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4241 var buttons, activeTextEl, bwidth;
4245 var handleButton = function(button){
4247 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4251 var handleHide = function(){
4253 dlg.el.removeClass(opt.cls);
4256 // Roo.TaskMgr.stop(waitTimer);
4257 // waitTimer = null;
4262 var updateButtons = function(b){
4265 buttons["ok"].hide();
4266 buttons["cancel"].hide();
4267 buttons["yes"].hide();
4268 buttons["no"].hide();
4269 dlg.footerEl.hide();
4273 dlg.footerEl.show();
4274 for(var k in buttons){
4275 if(typeof buttons[k] != "function"){
4278 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4279 width += buttons[k].el.getWidth()+15;
4289 var handleEsc = function(d, k, e){
4290 if(opt && opt.closable !== false){
4300 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4301 * @return {Roo.BasicDialog} The BasicDialog element
4303 getDialog : function(){
4305 dlg = new Roo.bootstrap.Modal( {
4308 //constraintoviewport:false,
4310 //collapsible : false,
4315 //buttonAlign:"center",
4316 closeClick : function(){
4317 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4320 handleButton("cancel");
4325 dlg.on("hide", handleHide);
4327 //dlg.addKeyListener(27, handleEsc);
4329 this.buttons = buttons;
4330 var bt = this.buttonText;
4331 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4332 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4333 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4334 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4336 bodyEl = dlg.bodyEl.createChild({
4338 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4339 '<textarea class="roo-mb-textarea"></textarea>' +
4340 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4342 msgEl = bodyEl.dom.firstChild;
4343 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4344 textboxEl.enableDisplayMode();
4345 textboxEl.addKeyListener([10,13], function(){
4346 if(dlg.isVisible() && opt && opt.buttons){
4349 }else if(opt.buttons.yes){
4350 handleButton("yes");
4354 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4355 textareaEl.enableDisplayMode();
4356 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4357 progressEl.enableDisplayMode();
4359 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4360 var pf = progressEl.dom.firstChild;
4362 pp = Roo.get(pf.firstChild);
4363 pp.setHeight(pf.offsetHeight);
4371 * Updates the message box body text
4372 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4373 * the XHTML-compliant non-breaking space character '&#160;')
4374 * @return {Roo.MessageBox} This message box
4376 updateText : function(text)
4378 if(!dlg.isVisible() && !opt.width){
4379 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4380 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4382 msgEl.innerHTML = text || ' ';
4384 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4385 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4387 Math.min(opt.width || cw , this.maxWidth),
4388 Math.max(opt.minWidth || this.minWidth, bwidth)
4391 activeTextEl.setWidth(w);
4393 if(dlg.isVisible()){
4394 dlg.fixedcenter = false;
4396 // to big, make it scroll. = But as usual stupid IE does not support
4399 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4400 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4401 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4403 bodyEl.dom.style.height = '';
4404 bodyEl.dom.style.overflowY = '';
4407 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4409 bodyEl.dom.style.overflowX = '';
4412 dlg.setContentSize(w, bodyEl.getHeight());
4413 if(dlg.isVisible()){
4414 dlg.fixedcenter = true;
4420 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4421 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4422 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4423 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4424 * @return {Roo.MessageBox} This message box
4426 updateProgress : function(value, text){
4428 this.updateText(text);
4431 if (pp) { // weird bug on my firefox - for some reason this is not defined
4432 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4433 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4439 * Returns true if the message box is currently displayed
4440 * @return {Boolean} True if the message box is visible, else false
4442 isVisible : function(){
4443 return dlg && dlg.isVisible();
4447 * Hides the message box if it is displayed
4450 if(this.isVisible()){
4456 * Displays a new message box, or reinitializes an existing message box, based on the config options
4457 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4458 * The following config object properties are supported:
4460 Property Type Description
4461 ---------- --------------- ------------------------------------------------------------------------------------
4462 animEl String/Element An id or Element from which the message box should animate as it opens and
4463 closes (defaults to undefined)
4464 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4465 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4466 closable Boolean False to hide the top-right close button (defaults to true). Note that
4467 progress and wait dialogs will ignore this property and always hide the
4468 close button as they can only be closed programmatically.
4469 cls String A custom CSS class to apply to the message box element
4470 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4471 displayed (defaults to 75)
4472 fn Function A callback function to execute after closing the dialog. The arguments to the
4473 function will be btn (the name of the button that was clicked, if applicable,
4474 e.g. "ok"), and text (the value of the active text field, if applicable).
4475 Progress and wait dialogs will ignore this option since they do not respond to
4476 user actions and can only be closed programmatically, so any required function
4477 should be called by the same code after it closes the dialog.
4478 icon String A CSS class that provides a background image to be used as an icon for
4479 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4480 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4481 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4482 modal Boolean False to allow user interaction with the page while the message box is
4483 displayed (defaults to true)
4484 msg String A string that will replace the existing message box body text (defaults
4485 to the XHTML-compliant non-breaking space character ' ')
4486 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4487 progress Boolean True to display a progress bar (defaults to false)
4488 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4489 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4490 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4491 title String The title text
4492 value String The string value to set into the active textbox element if displayed
4493 wait Boolean True to display a progress bar (defaults to false)
4494 width Number The width of the dialog in pixels
4501 msg: 'Please enter your address:',
4503 buttons: Roo.MessageBox.OKCANCEL,
4506 animEl: 'addAddressBtn'
4509 * @param {Object} config Configuration options
4510 * @return {Roo.MessageBox} This message box
4512 show : function(options)
4515 // this causes nightmares if you show one dialog after another
4516 // especially on callbacks..
4518 if(this.isVisible()){
4521 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4522 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4523 Roo.log("New Dialog Message:" + options.msg )
4524 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4525 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4528 var d = this.getDialog();
4530 d.setTitle(opt.title || " ");
4531 d.closeEl.setDisplayed(opt.closable !== false);
4532 activeTextEl = textboxEl;
4533 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4538 textareaEl.setHeight(typeof opt.multiline == "number" ?
4539 opt.multiline : this.defaultTextHeight);
4540 activeTextEl = textareaEl;
4549 progressEl.setDisplayed(opt.progress === true);
4551 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4553 this.updateProgress(0);
4554 activeTextEl.dom.value = opt.value || "";
4556 dlg.setDefaultButton(activeTextEl);
4558 var bs = opt.buttons;
4562 }else if(bs && bs.yes){
4563 db = buttons["yes"];
4565 dlg.setDefaultButton(db);
4567 bwidth = updateButtons(opt.buttons);
4568 this.updateText(opt.msg);
4570 d.el.addClass(opt.cls);
4572 d.proxyDrag = opt.proxyDrag === true;
4573 d.modal = opt.modal !== false;
4574 d.mask = opt.modal !== false ? mask : false;
4576 // force it to the end of the z-index stack so it gets a cursor in FF
4577 document.body.appendChild(dlg.el.dom);
4578 d.animateTarget = null;
4579 d.show(options.animEl);
4585 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4586 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4587 * and closing the message box when the process is complete.
4588 * @param {String} title The title bar text
4589 * @param {String} msg The message box body text
4590 * @return {Roo.MessageBox} This message box
4592 progress : function(title, msg){
4599 minWidth: this.minProgressWidth,
4606 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4607 * If a callback function is passed it will be called after the user clicks the button, and the
4608 * id of the button that was clicked will be passed as the only parameter to the callback
4609 * (could also be the top-right close button).
4610 * @param {String} title The title bar text
4611 * @param {String} msg The message box body text
4612 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4613 * @param {Object} scope (optional) The scope of the callback function
4614 * @return {Roo.MessageBox} This message box
4616 alert : function(title, msg, fn, scope)
4631 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4632 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4633 * You are responsible for closing the message box when the process is complete.
4634 * @param {String} msg The message box body text
4635 * @param {String} title (optional) The title bar text
4636 * @return {Roo.MessageBox} This message box
4638 wait : function(msg, title){
4649 waitTimer = Roo.TaskMgr.start({
4651 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4659 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4660 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4661 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4662 * @param {String} title The title bar text
4663 * @param {String} msg The message box body text
4664 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4665 * @param {Object} scope (optional) The scope of the callback function
4666 * @return {Roo.MessageBox} This message box
4668 confirm : function(title, msg, fn, scope){
4672 buttons: this.YESNO,
4681 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4682 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4683 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4684 * (could also be the top-right close button) and the text that was entered will be passed as the two
4685 * parameters to the callback.
4686 * @param {String} title The title bar text
4687 * @param {String} msg The message box body text
4688 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4689 * @param {Object} scope (optional) The scope of the callback function
4690 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4691 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4692 * @return {Roo.MessageBox} This message box
4694 prompt : function(title, msg, fn, scope, multiline){
4698 buttons: this.OKCANCEL,
4703 multiline: multiline,
4710 * Button config that displays a single OK button
4715 * Button config that displays Yes and No buttons
4718 YESNO : {yes:true, no:true},
4720 * Button config that displays OK and Cancel buttons
4723 OKCANCEL : {ok:true, cancel:true},
4725 * Button config that displays Yes, No and Cancel buttons
4728 YESNOCANCEL : {yes:true, no:true, cancel:true},
4731 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4734 defaultTextHeight : 75,
4736 * The maximum width in pixels of the message box (defaults to 600)
4741 * The minimum width in pixels of the message box (defaults to 100)
4746 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4747 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4750 minProgressWidth : 250,
4752 * An object containing the default button text strings that can be overriden for localized language support.
4753 * Supported properties are: ok, cancel, yes and no.
4754 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4767 * Shorthand for {@link Roo.MessageBox}
4769 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4770 Roo.Msg = Roo.Msg || Roo.MessageBox;
4779 * @class Roo.bootstrap.Navbar
4780 * @extends Roo.bootstrap.Component
4781 * Bootstrap Navbar class
4784 * Create a new Navbar
4785 * @param {Object} config The config object
4789 Roo.bootstrap.Navbar = function(config){
4790 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4794 * @event beforetoggle
4795 * Fire before toggle the menu
4796 * @param {Roo.EventObject} e
4798 "beforetoggle" : true
4802 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4811 getAutoCreate : function(){
4814 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4818 initEvents :function ()
4820 //Roo.log(this.el.select('.navbar-toggle',true));
4821 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4828 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4830 var size = this.el.getSize();
4831 this.maskEl.setSize(size.width, size.height);
4832 this.maskEl.enableDisplayMode("block");
4841 getChildContainer : function()
4843 if (this.el && this.el.select('.collapse').getCount()) {
4844 return this.el.select('.collapse',true).first();
4859 onToggle : function()
4862 if(this.fireEvent('beforetoggle', this) === false){
4865 var ce = this.el.select('.navbar-collapse',true).first();
4867 if (!ce.hasClass('show')) {
4877 * Expand the navbar pulldown
4879 expand : function ()
4882 var ce = this.el.select('.navbar-collapse',true).first();
4883 if (ce.hasClass('collapsing')) {
4886 ce.dom.style.height = '';
4888 ce.addClass('in'); // old...
4889 ce.removeClass('collapse');
4890 ce.addClass('show');
4891 var h = ce.getHeight();
4893 ce.removeClass('show');
4894 // at this point we should be able to see it..
4895 ce.addClass('collapsing');
4897 ce.setHeight(0); // resize it ...
4898 ce.on('transitionend', function() {
4899 //Roo.log('done transition');
4900 ce.removeClass('collapsing');
4901 ce.addClass('show');
4902 ce.removeClass('collapse');
4904 ce.dom.style.height = '';
4905 }, this, { single: true} );
4907 ce.dom.scrollTop = 0;
4910 * Collapse the navbar pulldown
4912 collapse : function()
4914 var ce = this.el.select('.navbar-collapse',true).first();
4916 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4917 // it's collapsed or collapsing..
4920 ce.removeClass('in'); // old...
4921 ce.setHeight(ce.getHeight());
4922 ce.removeClass('show');
4923 ce.addClass('collapsing');
4925 ce.on('transitionend', function() {
4926 ce.dom.style.height = '';
4927 ce.removeClass('collapsing');
4928 ce.addClass('collapse');
4929 }, this, { single: true} );
4949 * @class Roo.bootstrap.NavSimplebar
4950 * @extends Roo.bootstrap.Navbar
4951 * Bootstrap Sidebar class
4953 * @cfg {Boolean} inverse is inverted color
4955 * @cfg {String} type (nav | pills | tabs)
4956 * @cfg {Boolean} arrangement stacked | justified
4957 * @cfg {String} align (left | right) alignment
4959 * @cfg {Boolean} main (true|false) main nav bar? default false
4960 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4962 * @cfg {String} tag (header|footer|nav|div) default is nav
4964 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4968 * Create a new Sidebar
4969 * @param {Object} config The config object
4973 Roo.bootstrap.NavSimplebar = function(config){
4974 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4977 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4993 getAutoCreate : function(){
4997 tag : this.tag || 'div',
4998 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5000 if (['light','white'].indexOf(this.weight) > -1) {
5001 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5003 cfg.cls += ' bg-' + this.weight;
5006 cfg.cls += ' navbar-inverse';
5010 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5012 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5021 cls: 'nav nav-' + this.xtype,
5027 this.type = this.type || 'nav';
5028 if (['tabs','pills'].indexOf(this.type) != -1) {
5029 cfg.cn[0].cls += ' nav-' + this.type
5033 if (this.type!=='nav') {
5034 Roo.log('nav type must be nav/tabs/pills')
5036 cfg.cn[0].cls += ' navbar-nav'
5042 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5043 cfg.cn[0].cls += ' nav-' + this.arrangement;
5047 if (this.align === 'right') {
5048 cfg.cn[0].cls += ' navbar-right';
5073 * navbar-expand-md fixed-top
5077 * @class Roo.bootstrap.NavHeaderbar
5078 * @extends Roo.bootstrap.NavSimplebar
5079 * Bootstrap Sidebar class
5081 * @cfg {String} brand what is brand
5082 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5083 * @cfg {String} brand_href href of the brand
5084 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5085 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5086 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5087 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5090 * Create a new Sidebar
5091 * @param {Object} config The config object
5095 Roo.bootstrap.NavHeaderbar = function(config){
5096 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5100 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5107 desktopCenter : false,
5110 getAutoCreate : function(){
5113 tag: this.nav || 'nav',
5114 cls: 'navbar navbar-expand-md',
5120 if (this.desktopCenter) {
5121 cn.push({cls : 'container', cn : []});
5129 cls: 'navbar-toggle navbar-toggler',
5130 'data-toggle': 'collapse',
5135 html: 'Toggle navigation'
5139 cls: 'icon-bar navbar-toggler-icon'
5152 cn.push( Roo.bootstrap.version == 4 ? btn : {
5154 cls: 'navbar-header',
5163 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5167 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5169 if (['light','white'].indexOf(this.weight) > -1) {
5170 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5172 cfg.cls += ' bg-' + this.weight;
5175 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5176 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5178 // tag can override this..
5180 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5183 if (this.brand !== '') {
5184 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5185 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5187 href: this.brand_href ? this.brand_href : '#',
5188 cls: 'navbar-brand',
5196 cfg.cls += ' main-nav';
5204 getHeaderChildContainer : function()
5206 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5207 return this.el.select('.navbar-header',true).first();
5210 return this.getChildContainer();
5213 getChildContainer : function()
5216 return this.el.select('.roo-navbar-collapse',true).first();
5221 initEvents : function()
5223 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5225 if (this.autohide) {
5230 Roo.get(document).on('scroll',function(e) {
5231 var ns = Roo.get(document).getScroll().top;
5232 var os = prevScroll;
5236 ft.removeClass('slideDown');
5237 ft.addClass('slideUp');
5240 ft.removeClass('slideUp');
5241 ft.addClass('slideDown');
5262 * @class Roo.bootstrap.NavSidebar
5263 * @extends Roo.bootstrap.Navbar
5264 * Bootstrap Sidebar class
5267 * Create a new Sidebar
5268 * @param {Object} config The config object
5272 Roo.bootstrap.NavSidebar = function(config){
5273 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5276 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5278 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5280 getAutoCreate : function(){
5285 cls: 'sidebar sidebar-nav'
5307 * @class Roo.bootstrap.NavGroup
5308 * @extends Roo.bootstrap.Component
5309 * Bootstrap NavGroup class
5310 * @cfg {String} align (left|right)
5311 * @cfg {Boolean} inverse
5312 * @cfg {String} type (nav|pills|tab) default nav
5313 * @cfg {String} navId - reference Id for navbar.
5317 * Create a new nav group
5318 * @param {Object} config The config object
5321 Roo.bootstrap.NavGroup = function(config){
5322 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5325 Roo.bootstrap.NavGroup.register(this);
5329 * Fires when the active item changes
5330 * @param {Roo.bootstrap.NavGroup} this
5331 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5332 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5339 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5350 getAutoCreate : function()
5352 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5358 if (Roo.bootstrap.version == 4) {
5359 if (['tabs','pills'].indexOf(this.type) != -1) {
5360 cfg.cls += ' nav-' + this.type;
5362 // trying to remove so header bar can right align top?
5363 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5364 // do not use on header bar...
5365 cfg.cls += ' navbar-nav';
5370 if (['tabs','pills'].indexOf(this.type) != -1) {
5371 cfg.cls += ' nav-' + this.type
5373 if (this.type !== 'nav') {
5374 Roo.log('nav type must be nav/tabs/pills')
5376 cfg.cls += ' navbar-nav'
5380 if (this.parent() && this.parent().sidebar) {
5383 cls: 'dashboard-menu sidebar-menu'
5389 if (this.form === true) {
5392 cls: 'navbar-form form-inline'
5394 //nav navbar-right ml-md-auto
5395 if (this.align === 'right') {
5396 cfg.cls += ' navbar-right ml-md-auto';
5398 cfg.cls += ' navbar-left';
5402 if (this.align === 'right') {
5403 cfg.cls += ' navbar-right ml-md-auto';
5405 cfg.cls += ' mr-auto';
5409 cfg.cls += ' navbar-inverse';
5417 * sets the active Navigation item
5418 * @param {Roo.bootstrap.NavItem} the new current navitem
5420 setActiveItem : function(item)
5423 Roo.each(this.navItems, function(v){
5428 v.setActive(false, true);
5435 item.setActive(true, true);
5436 this.fireEvent('changed', this, item, prev);
5441 * gets the active Navigation item
5442 * @return {Roo.bootstrap.NavItem} the current navitem
5444 getActive : function()
5448 Roo.each(this.navItems, function(v){
5459 indexOfNav : function()
5463 Roo.each(this.navItems, function(v,i){
5474 * adds a Navigation item
5475 * @param {Roo.bootstrap.NavItem} the navitem to add
5477 addItem : function(cfg)
5479 if (this.form && Roo.bootstrap.version == 4) {
5482 var cn = new Roo.bootstrap.NavItem(cfg);
5484 cn.parentId = this.id;
5485 cn.onRender(this.el, null);
5489 * register a Navigation item
5490 * @param {Roo.bootstrap.NavItem} the navitem to add
5492 register : function(item)
5494 this.navItems.push( item);
5495 item.navId = this.navId;
5500 * clear all the Navigation item
5503 clearAll : function()
5506 this.el.dom.innerHTML = '';
5509 getNavItem: function(tabId)
5512 Roo.each(this.navItems, function(e) {
5513 if (e.tabId == tabId) {
5523 setActiveNext : function()
5525 var i = this.indexOfNav(this.getActive());
5526 if (i > this.navItems.length) {
5529 this.setActiveItem(this.navItems[i+1]);
5531 setActivePrev : function()
5533 var i = this.indexOfNav(this.getActive());
5537 this.setActiveItem(this.navItems[i-1]);
5539 clearWasActive : function(except) {
5540 Roo.each(this.navItems, function(e) {
5541 if (e.tabId != except.tabId && e.was_active) {
5542 e.was_active = false;
5549 getWasActive : function ()
5552 Roo.each(this.navItems, function(e) {
5567 Roo.apply(Roo.bootstrap.NavGroup, {
5571 * register a Navigation Group
5572 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5574 register : function(navgrp)
5576 this.groups[navgrp.navId] = navgrp;
5580 * fetch a Navigation Group based on the navigation ID
5581 * @param {string} the navgroup to add
5582 * @returns {Roo.bootstrap.NavGroup} the navgroup
5584 get: function(navId) {
5585 if (typeof(this.groups[navId]) == 'undefined') {
5587 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5589 return this.groups[navId] ;
5604 * @class Roo.bootstrap.NavItem
5605 * @extends Roo.bootstrap.Component
5606 * Bootstrap Navbar.NavItem class
5607 * @cfg {String} href link to
5608 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5610 * @cfg {String} html content of button
5611 * @cfg {String} badge text inside badge
5612 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5613 * @cfg {String} glyphicon DEPRICATED - use fa
5614 * @cfg {String} icon DEPRICATED - use fa
5615 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5616 * @cfg {Boolean} active Is item active
5617 * @cfg {Boolean} disabled Is item disabled
5619 * @cfg {Boolean} preventDefault (true | false) default false
5620 * @cfg {String} tabId the tab that this item activates.
5621 * @cfg {String} tagtype (a|span) render as a href or span?
5622 * @cfg {Boolean} animateRef (true|false) link to element default false
5625 * Create a new Navbar Item
5626 * @param {Object} config The config object
5628 Roo.bootstrap.NavItem = function(config){
5629 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5634 * The raw click event for the entire grid.
5635 * @param {Roo.EventObject} e
5640 * Fires when the active item active state changes
5641 * @param {Roo.bootstrap.NavItem} this
5642 * @param {boolean} state the new state
5648 * Fires when scroll to element
5649 * @param {Roo.bootstrap.NavItem} this
5650 * @param {Object} options
5651 * @param {Roo.EventObject} e
5659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5668 preventDefault : false,
5676 button_outline : false,
5680 getAutoCreate : function(){
5688 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5690 if (this.disabled) {
5691 cfg.cls += ' disabled';
5695 if (this.button_weight.length) {
5696 cfg.tag = this.href ? 'a' : 'button';
5697 cfg.html = this.html || '';
5698 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5700 cfg.href = this.href;
5703 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5706 // menu .. should add dropdown-menu class - so no need for carat..
5708 if (this.badge !== '') {
5710 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5715 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5719 href : this.href || "#",
5720 html: this.html || ''
5723 if (this.tagtype == 'a') {
5724 cfg.cn[0].cls = 'nav-link';
5727 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5730 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5732 if(this.glyphicon) {
5733 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5738 cfg.cn[0].html += " <span class='caret'></span>";
5742 if (this.badge !== '') {
5744 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5752 onRender : function(ct, position)
5754 // Roo.log("Call onRender: " + this.xtype);
5755 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5759 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5760 this.navLink = this.el.select('.nav-link',true).first();
5765 initEvents: function()
5767 if (typeof (this.menu) != 'undefined') {
5768 this.menu.parentType = this.xtype;
5769 this.menu.triggerEl = this.el;
5770 this.menu = this.addxtype(Roo.apply({}, this.menu));
5773 this.el.select('a',true).on('click', this.onClick, this);
5775 if(this.tagtype == 'span'){
5776 this.el.select('span',true).on('click', this.onClick, this);
5779 // at this point parent should be available..
5780 this.parent().register(this);
5783 onClick : function(e)
5785 if (e.getTarget('.dropdown-menu-item')) {
5786 // did you click on a menu itemm.... - then don't trigger onclick..
5791 this.preventDefault ||
5794 Roo.log("NavItem - prevent Default?");
5798 if (this.disabled) {
5802 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5803 if (tg && tg.transition) {
5804 Roo.log("waiting for the transitionend");
5810 //Roo.log("fire event clicked");
5811 if(this.fireEvent('click', this, e) === false){
5815 if(this.tagtype == 'span'){
5819 //Roo.log(this.href);
5820 var ael = this.el.select('a',true).first();
5823 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5824 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5825 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5826 return; // ignore... - it's a 'hash' to another page.
5828 Roo.log("NavItem - prevent Default?");
5830 this.scrollToElement(e);
5834 var p = this.parent();
5836 if (['tabs','pills'].indexOf(p.type)!==-1) {
5837 if (typeof(p.setActiveItem) !== 'undefined') {
5838 p.setActiveItem(this);
5842 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5843 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5844 // remove the collapsed menu expand...
5845 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5849 isActive: function () {
5852 setActive : function(state, fire, is_was_active)
5854 if (this.active && !state && this.navId) {
5855 this.was_active = true;
5856 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5858 nv.clearWasActive(this);
5862 this.active = state;
5865 this.el.removeClass('active');
5866 this.navLink ? this.navLink.removeClass('active') : false;
5867 } else if (!this.el.hasClass('active')) {
5869 this.el.addClass('active');
5870 if (Roo.bootstrap.version == 4 && this.navLink ) {
5871 this.navLink.addClass('active');
5876 this.fireEvent('changed', this, state);
5879 // show a panel if it's registered and related..
5881 if (!this.navId || !this.tabId || !state || is_was_active) {
5885 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5889 var pan = tg.getPanelByName(this.tabId);
5893 // if we can not flip to new panel - go back to old nav highlight..
5894 if (false == tg.showPanel(pan)) {
5895 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5897 var onav = nv.getWasActive();
5899 onav.setActive(true, false, true);
5908 // this should not be here...
5909 setDisabled : function(state)
5911 this.disabled = state;
5913 this.el.removeClass('disabled');
5914 } else if (!this.el.hasClass('disabled')) {
5915 this.el.addClass('disabled');
5921 * Fetch the element to display the tooltip on.
5922 * @return {Roo.Element} defaults to this.el
5924 tooltipEl : function()
5926 return this.el.select('' + this.tagtype + '', true).first();
5929 scrollToElement : function(e)
5931 var c = document.body;
5934 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5936 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5937 c = document.documentElement;
5940 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5946 var o = target.calcOffsetsTo(c);
5953 this.fireEvent('scrollto', this, options, e);
5955 Roo.get(c).scrollTo('top', options.value, true);
5968 * <span> icon </span>
5969 * <span> text </span>
5970 * <span>badge </span>
5974 * @class Roo.bootstrap.NavSidebarItem
5975 * @extends Roo.bootstrap.NavItem
5976 * Bootstrap Navbar.NavSidebarItem class
5977 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5978 * {Boolean} open is the menu open
5979 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5980 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5981 * {String} buttonSize (sm|md|lg)the extra classes for the button
5982 * {Boolean} showArrow show arrow next to the text (default true)
5984 * Create a new Navbar Button
5985 * @param {Object} config The config object
5987 Roo.bootstrap.NavSidebarItem = function(config){
5988 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5993 * The raw click event for the entire grid.
5994 * @param {Roo.EventObject} e
5999 * Fires when the active item active state changes
6000 * @param {Roo.bootstrap.NavSidebarItem} this
6001 * @param {boolean} state the new state
6009 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6011 badgeWeight : 'default',
6017 buttonWeight : 'default',
6023 getAutoCreate : function(){
6028 href : this.href || '#',
6034 if(this.buttonView){
6037 href : this.href || '#',
6038 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6051 cfg.cls += ' active';
6054 if (this.disabled) {
6055 cfg.cls += ' disabled';
6058 cfg.cls += ' open x-open';
6061 if (this.glyphicon || this.icon) {
6062 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6063 a.cn.push({ tag : 'i', cls : c }) ;
6066 if(!this.buttonView){
6069 html : this.html || ''
6076 if (this.badge !== '') {
6077 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6083 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6086 a.cls += ' dropdown-toggle treeview' ;
6092 initEvents : function()
6094 if (typeof (this.menu) != 'undefined') {
6095 this.menu.parentType = this.xtype;
6096 this.menu.triggerEl = this.el;
6097 this.menu = this.addxtype(Roo.apply({}, this.menu));
6100 this.el.on('click', this.onClick, this);
6102 if(this.badge !== ''){
6103 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6108 onClick : function(e)
6115 if(this.preventDefault){
6119 this.fireEvent('click', this, e);
6122 disable : function()
6124 this.setDisabled(true);
6129 this.setDisabled(false);
6132 setDisabled : function(state)
6134 if(this.disabled == state){
6138 this.disabled = state;
6141 this.el.addClass('disabled');
6145 this.el.removeClass('disabled');
6150 setActive : function(state)
6152 if(this.active == state){
6156 this.active = state;
6159 this.el.addClass('active');
6163 this.el.removeClass('active');
6168 isActive: function ()
6173 setBadge : function(str)
6179 this.badgeEl.dom.innerHTML = str;
6196 * @class Roo.bootstrap.Row
6197 * @extends Roo.bootstrap.Component
6198 * Bootstrap Row class (contains columns...)
6202 * @param {Object} config The config object
6205 Roo.bootstrap.Row = function(config){
6206 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6209 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6211 getAutoCreate : function(){
6230 * @class Roo.bootstrap.Pagination
6231 * @extends Roo.bootstrap.Component
6232 * Bootstrap Pagination class
6233 * @cfg {String} size xs | sm | md | lg
6234 * @cfg {Boolean} inverse false | true
6237 * Create a new Pagination
6238 * @param {Object} config The config object
6241 Roo.bootstrap.Pagination = function(config){
6242 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6245 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6251 getAutoCreate : function(){
6257 cfg.cls += ' inverse';
6263 cfg.cls += " " + this.cls;
6281 * @class Roo.bootstrap.PaginationItem
6282 * @extends Roo.bootstrap.Component
6283 * Bootstrap PaginationItem class
6284 * @cfg {String} html text
6285 * @cfg {String} href the link
6286 * @cfg {Boolean} preventDefault (true | false) default true
6287 * @cfg {Boolean} active (true | false) default false
6288 * @cfg {Boolean} disabled default false
6292 * Create a new PaginationItem
6293 * @param {Object} config The config object
6297 Roo.bootstrap.PaginationItem = function(config){
6298 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6303 * The raw click event for the entire grid.
6304 * @param {Roo.EventObject} e
6310 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6314 preventDefault: true,
6319 getAutoCreate : function(){
6325 href : this.href ? this.href : '#',
6326 html : this.html ? this.html : ''
6336 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6340 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6346 initEvents: function() {
6348 this.el.on('click', this.onClick, this);
6351 onClick : function(e)
6353 Roo.log('PaginationItem on click ');
6354 if(this.preventDefault){
6362 this.fireEvent('click', this, e);
6378 * @class Roo.bootstrap.Slider
6379 * @extends Roo.bootstrap.Component
6380 * Bootstrap Slider class
6383 * Create a new Slider
6384 * @param {Object} config The config object
6387 Roo.bootstrap.Slider = function(config){
6388 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6391 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6393 getAutoCreate : function(){
6397 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6401 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6413 * Ext JS Library 1.1.1
6414 * Copyright(c) 2006-2007, Ext JS, LLC.
6416 * Originally Released Under LGPL - original licence link has changed is not relivant.
6419 * <script type="text/javascript">
6424 * @class Roo.grid.ColumnModel
6425 * @extends Roo.util.Observable
6426 * This is the default implementation of a ColumnModel used by the Grid. It defines
6427 * the columns in the grid.
6430 var colModel = new Roo.grid.ColumnModel([
6431 {header: "Ticker", width: 60, sortable: true, locked: true},
6432 {header: "Company Name", width: 150, sortable: true},
6433 {header: "Market Cap.", width: 100, sortable: true},
6434 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6435 {header: "Employees", width: 100, sortable: true, resizable: false}
6440 * The config options listed for this class are options which may appear in each
6441 * individual column definition.
6442 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6444 * @param {Object} config An Array of column config objects. See this class's
6445 * config objects for details.
6447 Roo.grid.ColumnModel = function(config){
6449 * The config passed into the constructor
6451 this.config = config;
6454 // if no id, create one
6455 // if the column does not have a dataIndex mapping,
6456 // map it to the order it is in the config
6457 for(var i = 0, len = config.length; i < len; i++){
6459 if(typeof c.dataIndex == "undefined"){
6462 if(typeof c.renderer == "string"){
6463 c.renderer = Roo.util.Format[c.renderer];
6465 if(typeof c.id == "undefined"){
6468 if(c.editor && c.editor.xtype){
6469 c.editor = Roo.factory(c.editor, Roo.grid);
6471 if(c.editor && c.editor.isFormField){
6472 c.editor = new Roo.grid.GridEditor(c.editor);
6474 this.lookup[c.id] = c;
6478 * The width of columns which have no width specified (defaults to 100)
6481 this.defaultWidth = 100;
6484 * Default sortable of columns which have no sortable specified (defaults to false)
6487 this.defaultSortable = false;
6491 * @event widthchange
6492 * Fires when the width of a column changes.
6493 * @param {ColumnModel} this
6494 * @param {Number} columnIndex The column index
6495 * @param {Number} newWidth The new width
6497 "widthchange": true,
6499 * @event headerchange
6500 * Fires when the text of a header changes.
6501 * @param {ColumnModel} this
6502 * @param {Number} columnIndex The column index
6503 * @param {Number} newText The new header text
6505 "headerchange": true,
6507 * @event hiddenchange
6508 * Fires when a column is hidden or "unhidden".
6509 * @param {ColumnModel} this
6510 * @param {Number} columnIndex The column index
6511 * @param {Boolean} hidden true if hidden, false otherwise
6513 "hiddenchange": true,
6515 * @event columnmoved
6516 * Fires when a column is moved.
6517 * @param {ColumnModel} this
6518 * @param {Number} oldIndex
6519 * @param {Number} newIndex
6521 "columnmoved" : true,
6523 * @event columlockchange
6524 * Fires when a column's locked state is changed
6525 * @param {ColumnModel} this
6526 * @param {Number} colIndex
6527 * @param {Boolean} locked true if locked
6529 "columnlockchange" : true
6531 Roo.grid.ColumnModel.superclass.constructor.call(this);
6533 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6535 * @cfg {String} header The header text to display in the Grid view.
6538 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6539 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6540 * specified, the column's index is used as an index into the Record's data Array.
6543 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6544 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6547 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6548 * Defaults to the value of the {@link #defaultSortable} property.
6549 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6552 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6555 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6558 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6561 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6564 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6565 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6566 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6567 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6570 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6573 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6576 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6579 * @cfg {String} cursor (Optional)
6582 * @cfg {String} tooltip (Optional)
6585 * @cfg {Number} xs (Optional)
6588 * @cfg {Number} sm (Optional)
6591 * @cfg {Number} md (Optional)
6594 * @cfg {Number} lg (Optional)
6597 * Returns the id of the column at the specified index.
6598 * @param {Number} index The column index
6599 * @return {String} the id
6601 getColumnId : function(index){
6602 return this.config[index].id;
6606 * Returns the column for a specified id.
6607 * @param {String} id The column id
6608 * @return {Object} the column
6610 getColumnById : function(id){
6611 return this.lookup[id];
6616 * Returns the column for a specified dataIndex.
6617 * @param {String} dataIndex The column dataIndex
6618 * @return {Object|Boolean} the column or false if not found
6620 getColumnByDataIndex: function(dataIndex){
6621 var index = this.findColumnIndex(dataIndex);
6622 return index > -1 ? this.config[index] : false;
6626 * Returns the index for a specified column id.
6627 * @param {String} id The column id
6628 * @return {Number} the index, or -1 if not found
6630 getIndexById : function(id){
6631 for(var i = 0, len = this.config.length; i < len; i++){
6632 if(this.config[i].id == id){
6640 * Returns the index for a specified column dataIndex.
6641 * @param {String} dataIndex The column dataIndex
6642 * @return {Number} the index, or -1 if not found
6645 findColumnIndex : function(dataIndex){
6646 for(var i = 0, len = this.config.length; i < len; i++){
6647 if(this.config[i].dataIndex == dataIndex){
6655 moveColumn : function(oldIndex, newIndex){
6656 var c = this.config[oldIndex];
6657 this.config.splice(oldIndex, 1);
6658 this.config.splice(newIndex, 0, c);
6659 this.dataMap = null;
6660 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6663 isLocked : function(colIndex){
6664 return this.config[colIndex].locked === true;
6667 setLocked : function(colIndex, value, suppressEvent){
6668 if(this.isLocked(colIndex) == value){
6671 this.config[colIndex].locked = value;
6673 this.fireEvent("columnlockchange", this, colIndex, value);
6677 getTotalLockedWidth : function(){
6679 for(var i = 0; i < this.config.length; i++){
6680 if(this.isLocked(i) && !this.isHidden(i)){
6681 this.totalWidth += this.getColumnWidth(i);
6687 getLockedCount : function(){
6688 for(var i = 0, len = this.config.length; i < len; i++){
6689 if(!this.isLocked(i)){
6694 return this.config.length;
6698 * Returns the number of columns.
6701 getColumnCount : function(visibleOnly){
6702 if(visibleOnly === true){
6704 for(var i = 0, len = this.config.length; i < len; i++){
6705 if(!this.isHidden(i)){
6711 return this.config.length;
6715 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6716 * @param {Function} fn
6717 * @param {Object} scope (optional)
6718 * @return {Array} result
6720 getColumnsBy : function(fn, scope){
6722 for(var i = 0, len = this.config.length; i < len; i++){
6723 var c = this.config[i];
6724 if(fn.call(scope||this, c, i) === true){
6732 * Returns true if the specified column is sortable.
6733 * @param {Number} col The column index
6736 isSortable : function(col){
6737 if(typeof this.config[col].sortable == "undefined"){
6738 return this.defaultSortable;
6740 return this.config[col].sortable;
6744 * Returns the rendering (formatting) function defined for the column.
6745 * @param {Number} col The column index.
6746 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6748 getRenderer : function(col){
6749 if(!this.config[col].renderer){
6750 return Roo.grid.ColumnModel.defaultRenderer;
6752 return this.config[col].renderer;
6756 * Sets the rendering (formatting) function for a column.
6757 * @param {Number} col The column index
6758 * @param {Function} fn The function to use to process the cell's raw data
6759 * to return HTML markup for the grid view. The render function is called with
6760 * the following parameters:<ul>
6761 * <li>Data value.</li>
6762 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6763 * <li>css A CSS style string to apply to the table cell.</li>
6764 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6765 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6766 * <li>Row index</li>
6767 * <li>Column index</li>
6768 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6770 setRenderer : function(col, fn){
6771 this.config[col].renderer = fn;
6775 * Returns the width for the specified column.
6776 * @param {Number} col The column index
6779 getColumnWidth : function(col){
6780 return this.config[col].width * 1 || this.defaultWidth;
6784 * Sets the width for a column.
6785 * @param {Number} col The column index
6786 * @param {Number} width The new width
6788 setColumnWidth : function(col, width, suppressEvent){
6789 this.config[col].width = width;
6790 this.totalWidth = null;
6792 this.fireEvent("widthchange", this, col, width);
6797 * Returns the total width of all columns.
6798 * @param {Boolean} includeHidden True to include hidden column widths
6801 getTotalWidth : function(includeHidden){
6802 if(!this.totalWidth){
6803 this.totalWidth = 0;
6804 for(var i = 0, len = this.config.length; i < len; i++){
6805 if(includeHidden || !this.isHidden(i)){
6806 this.totalWidth += this.getColumnWidth(i);
6810 return this.totalWidth;
6814 * Returns the header for the specified column.
6815 * @param {Number} col The column index
6818 getColumnHeader : function(col){
6819 return this.config[col].header;
6823 * Sets the header for a column.
6824 * @param {Number} col The column index
6825 * @param {String} header The new header
6827 setColumnHeader : function(col, header){
6828 this.config[col].header = header;
6829 this.fireEvent("headerchange", this, col, header);
6833 * Returns the tooltip for the specified column.
6834 * @param {Number} col The column index
6837 getColumnTooltip : function(col){
6838 return this.config[col].tooltip;
6841 * Sets the tooltip for a column.
6842 * @param {Number} col The column index
6843 * @param {String} tooltip The new tooltip
6845 setColumnTooltip : function(col, tooltip){
6846 this.config[col].tooltip = tooltip;
6850 * Returns the dataIndex for the specified column.
6851 * @param {Number} col The column index
6854 getDataIndex : function(col){
6855 return this.config[col].dataIndex;
6859 * Sets the dataIndex for a column.
6860 * @param {Number} col The column index
6861 * @param {Number} dataIndex The new dataIndex
6863 setDataIndex : function(col, dataIndex){
6864 this.config[col].dataIndex = dataIndex;
6870 * Returns true if the cell is editable.
6871 * @param {Number} colIndex The column index
6872 * @param {Number} rowIndex The row index - this is nto actually used..?
6875 isCellEditable : function(colIndex, rowIndex){
6876 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6880 * Returns the editor defined for the cell/column.
6881 * return false or null to disable editing.
6882 * @param {Number} colIndex The column index
6883 * @param {Number} rowIndex The row index
6886 getCellEditor : function(colIndex, rowIndex){
6887 return this.config[colIndex].editor;
6891 * Sets if a column is editable.
6892 * @param {Number} col The column index
6893 * @param {Boolean} editable True if the column is editable
6895 setEditable : function(col, editable){
6896 this.config[col].editable = editable;
6901 * Returns true if the column is hidden.
6902 * @param {Number} colIndex The column index
6905 isHidden : function(colIndex){
6906 return this.config[colIndex].hidden;
6911 * Returns true if the column width cannot be changed
6913 isFixed : function(colIndex){
6914 return this.config[colIndex].fixed;
6918 * Returns true if the column can be resized
6921 isResizable : function(colIndex){
6922 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6925 * Sets if a column is hidden.
6926 * @param {Number} colIndex The column index
6927 * @param {Boolean} hidden True if the column is hidden
6929 setHidden : function(colIndex, hidden){
6930 this.config[colIndex].hidden = hidden;
6931 this.totalWidth = null;
6932 this.fireEvent("hiddenchange", this, colIndex, hidden);
6936 * Sets the editor for a column.
6937 * @param {Number} col The column index
6938 * @param {Object} editor The editor object
6940 setEditor : function(col, editor){
6941 this.config[col].editor = editor;
6945 Roo.grid.ColumnModel.defaultRenderer = function(value)
6947 if(typeof value == "object") {
6950 if(typeof value == "string" && value.length < 1){
6954 return String.format("{0}", value);
6957 // Alias for backwards compatibility
6958 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6961 * Ext JS Library 1.1.1
6962 * Copyright(c) 2006-2007, Ext JS, LLC.
6964 * Originally Released Under LGPL - original licence link has changed is not relivant.
6967 * <script type="text/javascript">
6971 * @class Roo.LoadMask
6972 * A simple utility class for generically masking elements while loading data. If the element being masked has
6973 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6974 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6975 * element's UpdateManager load indicator and will be destroyed after the initial load.
6977 * Create a new LoadMask
6978 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6979 * @param {Object} config The config object
6981 Roo.LoadMask = function(el, config){
6982 this.el = Roo.get(el);
6983 Roo.apply(this, config);
6985 this.store.on('beforeload', this.onBeforeLoad, this);
6986 this.store.on('load', this.onLoad, this);
6987 this.store.on('loadexception', this.onLoadException, this);
6988 this.removeMask = false;
6990 var um = this.el.getUpdateManager();
6991 um.showLoadIndicator = false; // disable the default indicator
6992 um.on('beforeupdate', this.onBeforeLoad, this);
6993 um.on('update', this.onLoad, this);
6994 um.on('failure', this.onLoad, this);
6995 this.removeMask = true;
6999 Roo.LoadMask.prototype = {
7001 * @cfg {Boolean} removeMask
7002 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7003 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7007 * The text to display in a centered loading message box (defaults to 'Loading...')
7011 * @cfg {String} msgCls
7012 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7014 msgCls : 'x-mask-loading',
7017 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7023 * Disables the mask to prevent it from being displayed
7025 disable : function(){
7026 this.disabled = true;
7030 * Enables the mask so that it can be displayed
7032 enable : function(){
7033 this.disabled = false;
7036 onLoadException : function()
7040 if (typeof(arguments[3]) != 'undefined') {
7041 Roo.MessageBox.alert("Error loading",arguments[3]);
7045 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7046 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7053 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7058 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7062 onBeforeLoad : function(){
7064 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7069 destroy : function(){
7071 this.store.un('beforeload', this.onBeforeLoad, this);
7072 this.store.un('load', this.onLoad, this);
7073 this.store.un('loadexception', this.onLoadException, this);
7075 var um = this.el.getUpdateManager();
7076 um.un('beforeupdate', this.onBeforeLoad, this);
7077 um.un('update', this.onLoad, this);
7078 um.un('failure', this.onLoad, this);
7089 * @class Roo.bootstrap.Table
7090 * @extends Roo.bootstrap.Component
7091 * Bootstrap Table class
7092 * @cfg {String} cls table class
7093 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7094 * @cfg {String} bgcolor Specifies the background color for a table
7095 * @cfg {Number} border Specifies whether the table cells should have borders or not
7096 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7097 * @cfg {Number} cellspacing Specifies the space between cells
7098 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7099 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7100 * @cfg {String} sortable Specifies that the table should be sortable
7101 * @cfg {String} summary Specifies a summary of the content of a table
7102 * @cfg {Number} width Specifies the width of a table
7103 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7105 * @cfg {boolean} striped Should the rows be alternative striped
7106 * @cfg {boolean} bordered Add borders to the table
7107 * @cfg {boolean} hover Add hover highlighting
7108 * @cfg {boolean} condensed Format condensed
7109 * @cfg {boolean} responsive Format condensed
7110 * @cfg {Boolean} loadMask (true|false) default false
7111 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7112 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7113 * @cfg {Boolean} rowSelection (true|false) default false
7114 * @cfg {Boolean} cellSelection (true|false) default false
7115 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7116 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7117 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7118 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7122 * Create a new Table
7123 * @param {Object} config The config object
7126 Roo.bootstrap.Table = function(config){
7127 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7132 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7133 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7134 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7135 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7137 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7139 this.sm.grid = this;
7140 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7141 this.sm = this.selModel;
7142 this.sm.xmodule = this.xmodule || false;
7145 if (this.cm && typeof(this.cm.config) == 'undefined') {
7146 this.colModel = new Roo.grid.ColumnModel(this.cm);
7147 this.cm = this.colModel;
7148 this.cm.xmodule = this.xmodule || false;
7151 this.store= Roo.factory(this.store, Roo.data);
7152 this.ds = this.store;
7153 this.ds.xmodule = this.xmodule || false;
7156 if (this.footer && this.store) {
7157 this.footer.dataSource = this.ds;
7158 this.footer = Roo.factory(this.footer);
7165 * Fires when a cell is clicked
7166 * @param {Roo.bootstrap.Table} this
7167 * @param {Roo.Element} el
7168 * @param {Number} rowIndex
7169 * @param {Number} columnIndex
7170 * @param {Roo.EventObject} e
7174 * @event celldblclick
7175 * Fires when a cell is double clicked
7176 * @param {Roo.bootstrap.Table} this
7177 * @param {Roo.Element} el
7178 * @param {Number} rowIndex
7179 * @param {Number} columnIndex
7180 * @param {Roo.EventObject} e
7182 "celldblclick" : true,
7185 * Fires when a row is clicked
7186 * @param {Roo.bootstrap.Table} this
7187 * @param {Roo.Element} el
7188 * @param {Number} rowIndex
7189 * @param {Roo.EventObject} e
7193 * @event rowdblclick
7194 * Fires when a row is double clicked
7195 * @param {Roo.bootstrap.Table} this
7196 * @param {Roo.Element} el
7197 * @param {Number} rowIndex
7198 * @param {Roo.EventObject} e
7200 "rowdblclick" : true,
7203 * Fires when a mouseover occur
7204 * @param {Roo.bootstrap.Table} this
7205 * @param {Roo.Element} el
7206 * @param {Number} rowIndex
7207 * @param {Number} columnIndex
7208 * @param {Roo.EventObject} e
7213 * Fires when a mouseout occur
7214 * @param {Roo.bootstrap.Table} this
7215 * @param {Roo.Element} el
7216 * @param {Number} rowIndex
7217 * @param {Number} columnIndex
7218 * @param {Roo.EventObject} e
7223 * Fires when a row is rendered, so you can change add a style to it.
7224 * @param {Roo.bootstrap.Table} this
7225 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7229 * @event rowsrendered
7230 * Fires when all the rows have been rendered
7231 * @param {Roo.bootstrap.Table} this
7233 'rowsrendered' : true,
7235 * @event contextmenu
7236 * The raw contextmenu event for the entire grid.
7237 * @param {Roo.EventObject} e
7239 "contextmenu" : true,
7241 * @event rowcontextmenu
7242 * Fires when a row is right clicked
7243 * @param {Roo.bootstrap.Table} this
7244 * @param {Number} rowIndex
7245 * @param {Roo.EventObject} e
7247 "rowcontextmenu" : true,
7249 * @event cellcontextmenu
7250 * Fires when a cell is right clicked
7251 * @param {Roo.bootstrap.Table} this
7252 * @param {Number} rowIndex
7253 * @param {Number} cellIndex
7254 * @param {Roo.EventObject} e
7256 "cellcontextmenu" : true,
7258 * @event headercontextmenu
7259 * Fires when a header is right clicked
7260 * @param {Roo.bootstrap.Table} this
7261 * @param {Number} columnIndex
7262 * @param {Roo.EventObject} e
7264 "headercontextmenu" : true
7268 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7294 rowSelection : false,
7295 cellSelection : false,
7298 // Roo.Element - the tbody
7300 // Roo.Element - thead element
7303 container: false, // used by gridpanel...
7309 auto_hide_footer : false,
7311 getAutoCreate : function()
7313 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7320 if (this.scrollBody) {
7321 cfg.cls += ' table-body-fixed';
7324 cfg.cls += ' table-striped';
7328 cfg.cls += ' table-hover';
7330 if (this.bordered) {
7331 cfg.cls += ' table-bordered';
7333 if (this.condensed) {
7334 cfg.cls += ' table-condensed';
7336 if (this.responsive) {
7337 cfg.cls += ' table-responsive';
7341 cfg.cls+= ' ' +this.cls;
7344 // this lot should be simplifed...
7357 ].forEach(function(k) {
7365 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7368 if(this.store || this.cm){
7369 if(this.headerShow){
7370 cfg.cn.push(this.renderHeader());
7373 cfg.cn.push(this.renderBody());
7375 if(this.footerShow){
7376 cfg.cn.push(this.renderFooter());
7378 // where does this come from?
7379 //cfg.cls+= ' TableGrid';
7382 return { cn : [ cfg ] };
7385 initEvents : function()
7387 if(!this.store || !this.cm){
7390 if (this.selModel) {
7391 this.selModel.initEvents();
7395 //Roo.log('initEvents with ds!!!!');
7397 this.mainBody = this.el.select('tbody', true).first();
7398 this.mainHead = this.el.select('thead', true).first();
7399 this.mainFoot = this.el.select('tfoot', true).first();
7405 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7406 e.on('click', _this.sort, _this);
7409 this.mainBody.on("click", this.onClick, this);
7410 this.mainBody.on("dblclick", this.onDblClick, this);
7412 // why is this done????? = it breaks dialogs??
7413 //this.parent().el.setStyle('position', 'relative');
7417 this.footer.parentId = this.id;
7418 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7421 this.el.select('tfoot tr td').first().addClass('hide');
7426 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7429 this.store.on('load', this.onLoad, this);
7430 this.store.on('beforeload', this.onBeforeLoad, this);
7431 this.store.on('update', this.onUpdate, this);
7432 this.store.on('add', this.onAdd, this);
7433 this.store.on("clear", this.clear, this);
7435 this.el.on("contextmenu", this.onContextMenu, this);
7437 this.mainBody.on('scroll', this.onBodyScroll, this);
7439 this.cm.on("headerchange", this.onHeaderChange, this);
7441 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7445 onContextMenu : function(e, t)
7447 this.processEvent("contextmenu", e);
7450 processEvent : function(name, e)
7452 if (name != 'touchstart' ) {
7453 this.fireEvent(name, e);
7456 var t = e.getTarget();
7458 var cell = Roo.get(t);
7464 if(cell.findParent('tfoot', false, true)){
7468 if(cell.findParent('thead', false, true)){
7470 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7471 cell = Roo.get(t).findParent('th', false, true);
7473 Roo.log("failed to find th in thead?");
7474 Roo.log(e.getTarget());
7479 var cellIndex = cell.dom.cellIndex;
7481 var ename = name == 'touchstart' ? 'click' : name;
7482 this.fireEvent("header" + ename, this, cellIndex, e);
7487 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7488 cell = Roo.get(t).findParent('td', false, true);
7490 Roo.log("failed to find th in tbody?");
7491 Roo.log(e.getTarget());
7496 var row = cell.findParent('tr', false, true);
7497 var cellIndex = cell.dom.cellIndex;
7498 var rowIndex = row.dom.rowIndex - 1;
7502 this.fireEvent("row" + name, this, rowIndex, e);
7506 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7512 onMouseover : function(e, el)
7514 var cell = Roo.get(el);
7520 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7521 cell = cell.findParent('td', false, true);
7524 var row = cell.findParent('tr', false, true);
7525 var cellIndex = cell.dom.cellIndex;
7526 var rowIndex = row.dom.rowIndex - 1; // start from 0
7528 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7532 onMouseout : function(e, el)
7534 var cell = Roo.get(el);
7540 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7541 cell = cell.findParent('td', false, true);
7544 var row = cell.findParent('tr', false, true);
7545 var cellIndex = cell.dom.cellIndex;
7546 var rowIndex = row.dom.rowIndex - 1; // start from 0
7548 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7552 onClick : function(e, el)
7554 var cell = Roo.get(el);
7556 if(!cell || (!this.cellSelection && !this.rowSelection)){
7560 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7561 cell = cell.findParent('td', false, true);
7564 if(!cell || typeof(cell) == 'undefined'){
7568 var row = cell.findParent('tr', false, true);
7570 if(!row || typeof(row) == 'undefined'){
7574 var cellIndex = cell.dom.cellIndex;
7575 var rowIndex = this.getRowIndex(row);
7577 // why??? - should these not be based on SelectionModel?
7578 if(this.cellSelection){
7579 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7582 if(this.rowSelection){
7583 this.fireEvent('rowclick', this, row, rowIndex, e);
7589 onDblClick : function(e,el)
7591 var cell = Roo.get(el);
7593 if(!cell || (!this.cellSelection && !this.rowSelection)){
7597 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7598 cell = cell.findParent('td', false, true);
7601 if(!cell || typeof(cell) == 'undefined'){
7605 var row = cell.findParent('tr', false, true);
7607 if(!row || typeof(row) == 'undefined'){
7611 var cellIndex = cell.dom.cellIndex;
7612 var rowIndex = this.getRowIndex(row);
7614 if(this.cellSelection){
7615 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7618 if(this.rowSelection){
7619 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7623 sort : function(e,el)
7625 var col = Roo.get(el);
7627 if(!col.hasClass('sortable')){
7631 var sort = col.attr('sort');
7634 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7638 this.store.sortInfo = {field : sort, direction : dir};
7641 Roo.log("calling footer first");
7642 this.footer.onClick('first');
7645 this.store.load({ params : { start : 0 } });
7649 renderHeader : function()
7657 this.totalWidth = 0;
7659 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7661 var config = cm.config[i];
7665 cls : 'x-hcol-' + i,
7667 html: cm.getColumnHeader(i)
7672 if(typeof(config.sortable) != 'undefined' && config.sortable){
7674 c.html = '<i class="glyphicon"></i>' + c.html;
7677 // could use BS4 hidden-..-down
7679 if(typeof(config.lgHeader) != 'undefined'){
7680 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7683 if(typeof(config.mdHeader) != 'undefined'){
7684 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7687 if(typeof(config.smHeader) != 'undefined'){
7688 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7691 if(typeof(config.xsHeader) != 'undefined'){
7692 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7699 if(typeof(config.tooltip) != 'undefined'){
7700 c.tooltip = config.tooltip;
7703 if(typeof(config.colspan) != 'undefined'){
7704 c.colspan = config.colspan;
7707 if(typeof(config.hidden) != 'undefined' && config.hidden){
7708 c.style += ' display:none;';
7711 if(typeof(config.dataIndex) != 'undefined'){
7712 c.sort = config.dataIndex;
7717 if(typeof(config.align) != 'undefined' && config.align.length){
7718 c.style += ' text-align:' + config.align + ';';
7721 if(typeof(config.width) != 'undefined'){
7722 c.style += ' width:' + config.width + 'px;';
7723 this.totalWidth += config.width;
7725 this.totalWidth += 100; // assume minimum of 100 per column?
7728 if(typeof(config.cls) != 'undefined'){
7729 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7732 ['xs','sm','md','lg'].map(function(size){
7734 if(typeof(config[size]) == 'undefined'){
7738 if (!config[size]) { // 0 = hidden
7739 // BS 4 '0' is treated as hide that column and below.
7740 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7744 c.cls += ' col-' + size + '-' + config[size] + (
7745 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7757 renderBody : function()
7767 colspan : this.cm.getColumnCount()
7777 renderFooter : function()
7787 colspan : this.cm.getColumnCount()
7801 // Roo.log('ds onload');
7806 var ds = this.store;
7808 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7809 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7810 if (_this.store.sortInfo) {
7812 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7813 e.select('i', true).addClass(['glyphicon-arrow-up']);
7816 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7817 e.select('i', true).addClass(['glyphicon-arrow-down']);
7822 var tbody = this.mainBody;
7824 if(ds.getCount() > 0){
7825 ds.data.each(function(d,rowIndex){
7826 var row = this.renderRow(cm, ds, rowIndex);
7828 tbody.createChild(row);
7832 if(row.cellObjects.length){
7833 Roo.each(row.cellObjects, function(r){
7834 _this.renderCellObject(r);
7841 var tfoot = this.el.select('tfoot', true).first();
7843 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7845 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7847 var total = this.ds.getTotalCount();
7849 if(this.footer.pageSize < total){
7850 this.mainFoot.show();
7854 Roo.each(this.el.select('tbody td', true).elements, function(e){
7855 e.on('mouseover', _this.onMouseover, _this);
7858 Roo.each(this.el.select('tbody td', true).elements, function(e){
7859 e.on('mouseout', _this.onMouseout, _this);
7861 this.fireEvent('rowsrendered', this);
7867 onUpdate : function(ds,record)
7869 this.refreshRow(record);
7873 onRemove : function(ds, record, index, isUpdate){
7874 if(isUpdate !== true){
7875 this.fireEvent("beforerowremoved", this, index, record);
7877 var bt = this.mainBody.dom;
7879 var rows = this.el.select('tbody > tr', true).elements;
7881 if(typeof(rows[index]) != 'undefined'){
7882 bt.removeChild(rows[index].dom);
7885 // if(bt.rows[index]){
7886 // bt.removeChild(bt.rows[index]);
7889 if(isUpdate !== true){
7890 //this.stripeRows(index);
7891 //this.syncRowHeights(index, index);
7893 this.fireEvent("rowremoved", this, index, record);
7897 onAdd : function(ds, records, rowIndex)
7899 //Roo.log('on Add called');
7900 // - note this does not handle multiple adding very well..
7901 var bt = this.mainBody.dom;
7902 for (var i =0 ; i < records.length;i++) {
7903 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7904 //Roo.log(records[i]);
7905 //Roo.log(this.store.getAt(rowIndex+i));
7906 this.insertRow(this.store, rowIndex + i, false);
7913 refreshRow : function(record){
7914 var ds = this.store, index;
7915 if(typeof record == 'number'){
7917 record = ds.getAt(index);
7919 index = ds.indexOf(record);
7921 this.insertRow(ds, index, true);
7923 this.onRemove(ds, record, index+1, true);
7925 //this.syncRowHeights(index, index);
7927 this.fireEvent("rowupdated", this, index, record);
7930 insertRow : function(dm, rowIndex, isUpdate){
7933 this.fireEvent("beforerowsinserted", this, rowIndex);
7935 //var s = this.getScrollState();
7936 var row = this.renderRow(this.cm, this.store, rowIndex);
7937 // insert before rowIndex..
7938 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7942 if(row.cellObjects.length){
7943 Roo.each(row.cellObjects, function(r){
7944 _this.renderCellObject(r);
7949 this.fireEvent("rowsinserted", this, rowIndex);
7950 //this.syncRowHeights(firstRow, lastRow);
7951 //this.stripeRows(firstRow);
7958 getRowDom : function(rowIndex)
7960 var rows = this.el.select('tbody > tr', true).elements;
7962 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7965 // returns the object tree for a tr..
7968 renderRow : function(cm, ds, rowIndex)
7970 var d = ds.getAt(rowIndex);
7974 cls : 'x-row-' + rowIndex,
7978 var cellObjects = [];
7980 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7981 var config = cm.config[i];
7983 var renderer = cm.getRenderer(i);
7987 if(typeof(renderer) !== 'undefined'){
7988 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7990 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7991 // and are rendered into the cells after the row is rendered - using the id for the element.
7993 if(typeof(value) === 'object'){
8003 rowIndex : rowIndex,
8008 this.fireEvent('rowclass', this, rowcfg);
8012 cls : rowcfg.rowClass + ' x-col-' + i,
8014 html: (typeof(value) === 'object') ? '' : value
8021 if(typeof(config.colspan) != 'undefined'){
8022 td.colspan = config.colspan;
8025 if(typeof(config.hidden) != 'undefined' && config.hidden){
8026 td.style += ' display:none;';
8029 if(typeof(config.align) != 'undefined' && config.align.length){
8030 td.style += ' text-align:' + config.align + ';';
8032 if(typeof(config.valign) != 'undefined' && config.valign.length){
8033 td.style += ' vertical-align:' + config.valign + ';';
8036 if(typeof(config.width) != 'undefined'){
8037 td.style += ' width:' + config.width + 'px;';
8040 if(typeof(config.cursor) != 'undefined'){
8041 td.style += ' cursor:' + config.cursor + ';';
8044 if(typeof(config.cls) != 'undefined'){
8045 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8048 ['xs','sm','md','lg'].map(function(size){
8050 if(typeof(config[size]) == 'undefined'){
8056 if (!config[size]) { // 0 = hidden
8057 // BS 4 '0' is treated as hide that column and below.
8058 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8062 td.cls += ' col-' + size + '-' + config[size] + (
8063 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8073 row.cellObjects = cellObjects;
8081 onBeforeLoad : function()
8090 this.el.select('tbody', true).first().dom.innerHTML = '';
8093 * Show or hide a row.
8094 * @param {Number} rowIndex to show or hide
8095 * @param {Boolean} state hide
8097 setRowVisibility : function(rowIndex, state)
8099 var bt = this.mainBody.dom;
8101 var rows = this.el.select('tbody > tr', true).elements;
8103 if(typeof(rows[rowIndex]) == 'undefined'){
8106 rows[rowIndex].dom.style.display = state ? '' : 'none';
8110 getSelectionModel : function(){
8112 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8114 return this.selModel;
8117 * Render the Roo.bootstrap object from renderder
8119 renderCellObject : function(r)
8123 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8125 var t = r.cfg.render(r.container);
8128 Roo.each(r.cfg.cn, function(c){
8130 container: t.getChildContainer(),
8133 _this.renderCellObject(child);
8138 getRowIndex : function(row)
8142 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8153 * Returns the grid's underlying element = used by panel.Grid
8154 * @return {Element} The element
8156 getGridEl : function(){
8160 * Forces a resize - used by panel.Grid
8161 * @return {Element} The element
8163 autoSize : function()
8165 //var ctr = Roo.get(this.container.dom.parentElement);
8166 var ctr = Roo.get(this.el.dom);
8168 var thd = this.getGridEl().select('thead',true).first();
8169 var tbd = this.getGridEl().select('tbody', true).first();
8170 var tfd = this.getGridEl().select('tfoot', true).first();
8172 var cw = ctr.getWidth();
8176 tbd.setWidth(ctr.getWidth());
8177 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8178 // this needs fixing for various usage - currently only hydra job advers I think..
8180 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8182 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8185 cw = Math.max(cw, this.totalWidth);
8186 this.getGridEl().select('tr',true).setWidth(cw);
8187 // resize 'expandable coloumn?
8189 return; // we doe not have a view in this design..
8192 onBodyScroll: function()
8194 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8196 this.mainHead.setStyle({
8197 'position' : 'relative',
8198 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8204 var scrollHeight = this.mainBody.dom.scrollHeight;
8206 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8208 var height = this.mainBody.getHeight();
8210 if(scrollHeight - height == scrollTop) {
8212 var total = this.ds.getTotalCount();
8214 if(this.footer.cursor + this.footer.pageSize < total){
8216 this.footer.ds.load({
8218 start : this.footer.cursor + this.footer.pageSize,
8219 limit : this.footer.pageSize
8229 onHeaderChange : function()
8231 var header = this.renderHeader();
8232 var table = this.el.select('table', true).first();
8234 this.mainHead.remove();
8235 this.mainHead = table.createChild(header, this.mainBody, false);
8238 onHiddenChange : function(colModel, colIndex, hidden)
8240 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8241 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8243 this.CSS.updateRule(thSelector, "display", "");
8244 this.CSS.updateRule(tdSelector, "display", "");
8247 this.CSS.updateRule(thSelector, "display", "none");
8248 this.CSS.updateRule(tdSelector, "display", "none");
8251 this.onHeaderChange();
8255 setColumnWidth: function(col_index, width)
8257 // width = "md-2 xs-2..."
8258 if(!this.colModel.config[col_index]) {
8262 var w = width.split(" ");
8264 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8266 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8269 for(var j = 0; j < w.length; j++) {
8275 var size_cls = w[j].split("-");
8277 if(!Number.isInteger(size_cls[1] * 1)) {
8281 if(!this.colModel.config[col_index][size_cls[0]]) {
8285 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8289 h_row[0].classList.replace(
8290 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8291 "col-"+size_cls[0]+"-"+size_cls[1]
8294 for(var i = 0; i < rows.length; i++) {
8296 var size_cls = w[j].split("-");
8298 if(!Number.isInteger(size_cls[1] * 1)) {
8302 if(!this.colModel.config[col_index][size_cls[0]]) {
8306 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8310 rows[i].classList.replace(
8311 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8312 "col-"+size_cls[0]+"-"+size_cls[1]
8316 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8331 * @class Roo.bootstrap.TableCell
8332 * @extends Roo.bootstrap.Component
8333 * Bootstrap TableCell class
8334 * @cfg {String} html cell contain text
8335 * @cfg {String} cls cell class
8336 * @cfg {String} tag cell tag (td|th) default td
8337 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8338 * @cfg {String} align Aligns the content in a cell
8339 * @cfg {String} axis Categorizes cells
8340 * @cfg {String} bgcolor Specifies the background color of a cell
8341 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8342 * @cfg {Number} colspan Specifies the number of columns a cell should span
8343 * @cfg {String} headers Specifies one or more header cells a cell is related to
8344 * @cfg {Number} height Sets the height of a cell
8345 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8346 * @cfg {Number} rowspan Sets the number of rows a cell should span
8347 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8348 * @cfg {String} valign Vertical aligns the content in a cell
8349 * @cfg {Number} width Specifies the width of a cell
8352 * Create a new TableCell
8353 * @param {Object} config The config object
8356 Roo.bootstrap.TableCell = function(config){
8357 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8360 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8380 getAutoCreate : function(){
8381 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8401 cfg.align=this.align
8407 cfg.bgcolor=this.bgcolor
8410 cfg.charoff=this.charoff
8413 cfg.colspan=this.colspan
8416 cfg.headers=this.headers
8419 cfg.height=this.height
8422 cfg.nowrap=this.nowrap
8425 cfg.rowspan=this.rowspan
8428 cfg.scope=this.scope
8431 cfg.valign=this.valign
8434 cfg.width=this.width
8453 * @class Roo.bootstrap.TableRow
8454 * @extends Roo.bootstrap.Component
8455 * Bootstrap TableRow class
8456 * @cfg {String} cls row class
8457 * @cfg {String} align Aligns the content in a table row
8458 * @cfg {String} bgcolor Specifies a background color for a table row
8459 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8460 * @cfg {String} valign Vertical aligns the content in a table row
8463 * Create a new TableRow
8464 * @param {Object} config The config object
8467 Roo.bootstrap.TableRow = function(config){
8468 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8471 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8479 getAutoCreate : function(){
8480 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8490 cfg.align = this.align;
8493 cfg.bgcolor = this.bgcolor;
8496 cfg.charoff = this.charoff;
8499 cfg.valign = this.valign;
8517 * @class Roo.bootstrap.TableBody
8518 * @extends Roo.bootstrap.Component
8519 * Bootstrap TableBody class
8520 * @cfg {String} cls element class
8521 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8522 * @cfg {String} align Aligns the content inside the element
8523 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8524 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8527 * Create a new TableBody
8528 * @param {Object} config The config object
8531 Roo.bootstrap.TableBody = function(config){
8532 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8535 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8543 getAutoCreate : function(){
8544 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8558 cfg.align = this.align;
8561 cfg.charoff = this.charoff;
8564 cfg.valign = this.valign;
8571 // initEvents : function()
8578 // this.store = Roo.factory(this.store, Roo.data);
8579 // this.store.on('load', this.onLoad, this);
8581 // this.store.load();
8585 // onLoad: function ()
8587 // this.fireEvent('load', this);
8597 * Ext JS Library 1.1.1
8598 * Copyright(c) 2006-2007, Ext JS, LLC.
8600 * Originally Released Under LGPL - original licence link has changed is not relivant.
8603 * <script type="text/javascript">
8606 // as we use this in bootstrap.
8607 Roo.namespace('Roo.form');
8609 * @class Roo.form.Action
8610 * Internal Class used to handle form actions
8612 * @param {Roo.form.BasicForm} el The form element or its id
8613 * @param {Object} config Configuration options
8618 // define the action interface
8619 Roo.form.Action = function(form, options){
8621 this.options = options || {};
8624 * Client Validation Failed
8627 Roo.form.Action.CLIENT_INVALID = 'client';
8629 * Server Validation Failed
8632 Roo.form.Action.SERVER_INVALID = 'server';
8634 * Connect to Server Failed
8637 Roo.form.Action.CONNECT_FAILURE = 'connect';
8639 * Reading Data from Server Failed
8642 Roo.form.Action.LOAD_FAILURE = 'load';
8644 Roo.form.Action.prototype = {
8646 failureType : undefined,
8647 response : undefined,
8651 run : function(options){
8656 success : function(response){
8661 handleResponse : function(response){
8665 // default connection failure
8666 failure : function(response){
8668 this.response = response;
8669 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8670 this.form.afterAction(this, false);
8673 processResponse : function(response){
8674 this.response = response;
8675 if(!response.responseText){
8678 this.result = this.handleResponse(response);
8682 // utility functions used internally
8683 getUrl : function(appendParams){
8684 var url = this.options.url || this.form.url || this.form.el.dom.action;
8686 var p = this.getParams();
8688 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8694 getMethod : function(){
8695 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8698 getParams : function(){
8699 var bp = this.form.baseParams;
8700 var p = this.options.params;
8702 if(typeof p == "object"){
8703 p = Roo.urlEncode(Roo.applyIf(p, bp));
8704 }else if(typeof p == 'string' && bp){
8705 p += '&' + Roo.urlEncode(bp);
8708 p = Roo.urlEncode(bp);
8713 createCallback : function(){
8715 success: this.success,
8716 failure: this.failure,
8718 timeout: (this.form.timeout*1000),
8719 upload: this.form.fileUpload ? this.success : undefined
8724 Roo.form.Action.Submit = function(form, options){
8725 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8728 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8731 haveProgress : false,
8732 uploadComplete : false,
8734 // uploadProgress indicator.
8735 uploadProgress : function()
8737 if (!this.form.progressUrl) {
8741 if (!this.haveProgress) {
8742 Roo.MessageBox.progress("Uploading", "Uploading");
8744 if (this.uploadComplete) {
8745 Roo.MessageBox.hide();
8749 this.haveProgress = true;
8751 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8753 var c = new Roo.data.Connection();
8755 url : this.form.progressUrl,
8760 success : function(req){
8761 //console.log(data);
8765 rdata = Roo.decode(req.responseText)
8767 Roo.log("Invalid data from server..");
8771 if (!rdata || !rdata.success) {
8773 Roo.MessageBox.alert(Roo.encode(rdata));
8776 var data = rdata.data;
8778 if (this.uploadComplete) {
8779 Roo.MessageBox.hide();
8784 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8785 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8788 this.uploadProgress.defer(2000,this);
8791 failure: function(data) {
8792 Roo.log('progress url failed ');
8803 // run get Values on the form, so it syncs any secondary forms.
8804 this.form.getValues();
8806 var o = this.options;
8807 var method = this.getMethod();
8808 var isPost = method == 'POST';
8809 if(o.clientValidation === false || this.form.isValid()){
8811 if (this.form.progressUrl) {
8812 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8813 (new Date() * 1) + '' + Math.random());
8818 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8819 form:this.form.el.dom,
8820 url:this.getUrl(!isPost),
8822 params:isPost ? this.getParams() : null,
8823 isUpload: this.form.fileUpload,
8824 formData : this.form.formData
8827 this.uploadProgress();
8829 }else if (o.clientValidation !== false){ // client validation failed
8830 this.failureType = Roo.form.Action.CLIENT_INVALID;
8831 this.form.afterAction(this, false);
8835 success : function(response)
8837 this.uploadComplete= true;
8838 if (this.haveProgress) {
8839 Roo.MessageBox.hide();
8843 var result = this.processResponse(response);
8844 if(result === true || result.success){
8845 this.form.afterAction(this, true);
8849 this.form.markInvalid(result.errors);
8850 this.failureType = Roo.form.Action.SERVER_INVALID;
8852 this.form.afterAction(this, false);
8854 failure : function(response)
8856 this.uploadComplete= true;
8857 if (this.haveProgress) {
8858 Roo.MessageBox.hide();
8861 this.response = response;
8862 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8863 this.form.afterAction(this, false);
8866 handleResponse : function(response){
8867 if(this.form.errorReader){
8868 var rs = this.form.errorReader.read(response);
8871 for(var i = 0, len = rs.records.length; i < len; i++) {
8872 var r = rs.records[i];
8876 if(errors.length < 1){
8880 success : rs.success,
8886 ret = Roo.decode(response.responseText);
8890 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8900 Roo.form.Action.Load = function(form, options){
8901 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8902 this.reader = this.form.reader;
8905 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8910 Roo.Ajax.request(Roo.apply(
8911 this.createCallback(), {
8912 method:this.getMethod(),
8913 url:this.getUrl(false),
8914 params:this.getParams()
8918 success : function(response){
8920 var result = this.processResponse(response);
8921 if(result === true || !result.success || !result.data){
8922 this.failureType = Roo.form.Action.LOAD_FAILURE;
8923 this.form.afterAction(this, false);
8926 this.form.clearInvalid();
8927 this.form.setValues(result.data);
8928 this.form.afterAction(this, true);
8931 handleResponse : function(response){
8932 if(this.form.reader){
8933 var rs = this.form.reader.read(response);
8934 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8936 success : rs.success,
8940 return Roo.decode(response.responseText);
8944 Roo.form.Action.ACTION_TYPES = {
8945 'load' : Roo.form.Action.Load,
8946 'submit' : Roo.form.Action.Submit
8955 * @class Roo.bootstrap.Form
8956 * @extends Roo.bootstrap.Component
8957 * Bootstrap Form class
8958 * @cfg {String} method GET | POST (default POST)
8959 * @cfg {String} labelAlign top | left (default top)
8960 * @cfg {String} align left | right - for navbars
8961 * @cfg {Boolean} loadMask load mask when submit (default true)
8966 * @param {Object} config The config object
8970 Roo.bootstrap.Form = function(config){
8972 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8974 Roo.bootstrap.Form.popover.apply();
8978 * @event clientvalidation
8979 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8980 * @param {Form} this
8981 * @param {Boolean} valid true if the form has passed client-side validation
8983 clientvalidation: true,
8985 * @event beforeaction
8986 * Fires before any action is performed. Return false to cancel the action.
8987 * @param {Form} this
8988 * @param {Action} action The action to be performed
8992 * @event actionfailed
8993 * Fires when an action fails.
8994 * @param {Form} this
8995 * @param {Action} action The action that failed
8997 actionfailed : true,
8999 * @event actioncomplete
9000 * Fires when an action is completed.
9001 * @param {Form} this
9002 * @param {Action} action The action that completed
9004 actioncomplete : true
9008 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9011 * @cfg {String} method
9012 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9017 * The URL to use for form actions if one isn't supplied in the action options.
9020 * @cfg {Boolean} fileUpload
9021 * Set to true if this form is a file upload.
9025 * @cfg {Object} baseParams
9026 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9030 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9034 * @cfg {Sting} align (left|right) for navbar forms
9039 activeAction : null,
9042 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9043 * element by passing it or its id or mask the form itself by passing in true.
9046 waitMsgTarget : false,
9051 * @cfg {Boolean} errorMask (true|false) default false
9056 * @cfg {Number} maskOffset Default 100
9061 * @cfg {Boolean} maskBody
9065 getAutoCreate : function(){
9069 method : this.method || 'POST',
9070 id : this.id || Roo.id(),
9073 if (this.parent().xtype.match(/^Nav/)) {
9074 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9078 if (this.labelAlign == 'left' ) {
9079 cfg.cls += ' form-horizontal';
9085 initEvents : function()
9087 this.el.on('submit', this.onSubmit, this);
9088 // this was added as random key presses on the form where triggering form submit.
9089 this.el.on('keypress', function(e) {
9090 if (e.getCharCode() != 13) {
9093 // we might need to allow it for textareas.. and some other items.
9094 // check e.getTarget().
9096 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9100 Roo.log("keypress blocked");
9108 onSubmit : function(e){
9113 * Returns true if client-side validation on the form is successful.
9116 isValid : function(){
9117 var items = this.getItems();
9121 items.each(function(f){
9127 Roo.log('invalid field: ' + f.name);
9131 if(!target && f.el.isVisible(true)){
9137 if(this.errorMask && !valid){
9138 Roo.bootstrap.Form.popover.mask(this, target);
9145 * Returns true if any fields in this form have changed since their original load.
9148 isDirty : function(){
9150 var items = this.getItems();
9151 items.each(function(f){
9161 * Performs a predefined action (submit or load) or custom actions you define on this form.
9162 * @param {String} actionName The name of the action type
9163 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9164 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9165 * accept other config options):
9167 Property Type Description
9168 ---------------- --------------- ----------------------------------------------------------------------------------
9169 url String The url for the action (defaults to the form's url)
9170 method String The form method to use (defaults to the form's method, or POST if not defined)
9171 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9172 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9173 validate the form on the client (defaults to false)
9175 * @return {BasicForm} this
9177 doAction : function(action, options){
9178 if(typeof action == 'string'){
9179 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9181 if(this.fireEvent('beforeaction', this, action) !== false){
9182 this.beforeAction(action);
9183 action.run.defer(100, action);
9189 beforeAction : function(action){
9190 var o = action.options;
9195 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9197 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9200 // not really supported yet.. ??
9202 //if(this.waitMsgTarget === true){
9203 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9204 //}else if(this.waitMsgTarget){
9205 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9206 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9208 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9214 afterAction : function(action, success){
9215 this.activeAction = null;
9216 var o = action.options;
9221 Roo.get(document.body).unmask();
9227 //if(this.waitMsgTarget === true){
9228 // this.el.unmask();
9229 //}else if(this.waitMsgTarget){
9230 // this.waitMsgTarget.unmask();
9232 // Roo.MessageBox.updateProgress(1);
9233 // Roo.MessageBox.hide();
9240 Roo.callback(o.success, o.scope, [this, action]);
9241 this.fireEvent('actioncomplete', this, action);
9245 // failure condition..
9246 // we have a scenario where updates need confirming.
9247 // eg. if a locking scenario exists..
9248 // we look for { errors : { needs_confirm : true }} in the response.
9250 (typeof(action.result) != 'undefined') &&
9251 (typeof(action.result.errors) != 'undefined') &&
9252 (typeof(action.result.errors.needs_confirm) != 'undefined')
9255 Roo.log("not supported yet");
9258 Roo.MessageBox.confirm(
9259 "Change requires confirmation",
9260 action.result.errorMsg,
9265 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9275 Roo.callback(o.failure, o.scope, [this, action]);
9276 // show an error message if no failed handler is set..
9277 if (!this.hasListener('actionfailed')) {
9278 Roo.log("need to add dialog support");
9280 Roo.MessageBox.alert("Error",
9281 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9282 action.result.errorMsg :
9283 "Saving Failed, please check your entries or try again"
9288 this.fireEvent('actionfailed', this, action);
9293 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9294 * @param {String} id The value to search for
9297 findField : function(id){
9298 var items = this.getItems();
9299 var field = items.get(id);
9301 items.each(function(f){
9302 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9309 return field || null;
9312 * Mark fields in this form invalid in bulk.
9313 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9314 * @return {BasicForm} this
9316 markInvalid : function(errors){
9317 if(errors instanceof Array){
9318 for(var i = 0, len = errors.length; i < len; i++){
9319 var fieldError = errors[i];
9320 var f = this.findField(fieldError.id);
9322 f.markInvalid(fieldError.msg);
9328 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9329 field.markInvalid(errors[id]);
9333 //Roo.each(this.childForms || [], function (f) {
9334 // f.markInvalid(errors);
9341 * Set values for fields in this form in bulk.
9342 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9343 * @return {BasicForm} this
9345 setValues : function(values){
9346 if(values instanceof Array){ // array of objects
9347 for(var i = 0, len = values.length; i < len; i++){
9349 var f = this.findField(v.id);
9351 f.setValue(v.value);
9352 if(this.trackResetOnLoad){
9353 f.originalValue = f.getValue();
9357 }else{ // object hash
9360 if(typeof values[id] != 'function' && (field = this.findField(id))){
9362 if (field.setFromData &&
9364 field.displayField &&
9365 // combos' with local stores can
9366 // be queried via setValue()
9367 // to set their value..
9368 (field.store && !field.store.isLocal)
9372 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9373 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9374 field.setFromData(sd);
9376 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9378 field.setFromData(values);
9381 field.setValue(values[id]);
9385 if(this.trackResetOnLoad){
9386 field.originalValue = field.getValue();
9392 //Roo.each(this.childForms || [], function (f) {
9393 // f.setValues(values);
9400 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9401 * they are returned as an array.
9402 * @param {Boolean} asString
9405 getValues : function(asString){
9406 //if (this.childForms) {
9407 // copy values from the child forms
9408 // Roo.each(this.childForms, function (f) {
9409 // this.setValues(f.getValues());
9415 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9416 if(asString === true){
9419 return Roo.urlDecode(fs);
9423 * Returns the fields in this form as an object with key/value pairs.
9424 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9427 getFieldValues : function(with_hidden)
9429 var items = this.getItems();
9431 items.each(function(f){
9437 var v = f.getValue();
9439 if (f.inputType =='radio') {
9440 if (typeof(ret[f.getName()]) == 'undefined') {
9441 ret[f.getName()] = ''; // empty..
9444 if (!f.el.dom.checked) {
9452 if(f.xtype == 'MoneyField'){
9453 ret[f.currencyName] = f.getCurrency();
9456 // not sure if this supported any more..
9457 if ((typeof(v) == 'object') && f.getRawValue) {
9458 v = f.getRawValue() ; // dates..
9460 // combo boxes where name != hiddenName...
9461 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9462 ret[f.name] = f.getRawValue();
9464 ret[f.getName()] = v;
9471 * Clears all invalid messages in this form.
9472 * @return {BasicForm} this
9474 clearInvalid : function(){
9475 var items = this.getItems();
9477 items.each(function(f){
9486 * @return {BasicForm} this
9489 var items = this.getItems();
9490 items.each(function(f){
9494 Roo.each(this.childForms || [], function (f) {
9502 getItems : function()
9504 var r=new Roo.util.MixedCollection(false, function(o){
9505 return o.id || (o.id = Roo.id());
9507 var iter = function(el) {
9514 Roo.each(el.items,function(e) {
9523 hideFields : function(items)
9525 Roo.each(items, function(i){
9527 var f = this.findField(i);
9538 showFields : function(items)
9540 Roo.each(items, function(i){
9542 var f = this.findField(i);
9555 Roo.apply(Roo.bootstrap.Form, {
9582 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9583 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9584 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9585 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9588 this.maskEl.top.enableDisplayMode("block");
9589 this.maskEl.left.enableDisplayMode("block");
9590 this.maskEl.bottom.enableDisplayMode("block");
9591 this.maskEl.right.enableDisplayMode("block");
9593 this.toolTip = new Roo.bootstrap.Tooltip({
9594 cls : 'roo-form-error-popover',
9596 'left' : ['r-l', [-2,0], 'right'],
9597 'right' : ['l-r', [2,0], 'left'],
9598 'bottom' : ['tl-bl', [0,2], 'top'],
9599 'top' : [ 'bl-tl', [0,-2], 'bottom']
9603 this.toolTip.render(Roo.get(document.body));
9605 this.toolTip.el.enableDisplayMode("block");
9607 Roo.get(document.body).on('click', function(){
9611 Roo.get(document.body).on('touchstart', function(){
9615 this.isApplied = true
9618 mask : function(form, target)
9622 this.target = target;
9624 if(!this.form.errorMask || !target.el){
9628 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9630 Roo.log(scrollable);
9632 var ot = this.target.el.calcOffsetsTo(scrollable);
9634 var scrollTo = ot[1] - this.form.maskOffset;
9636 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9638 scrollable.scrollTo('top', scrollTo);
9640 var box = this.target.el.getBox();
9642 var zIndex = Roo.bootstrap.Modal.zIndex++;
9645 this.maskEl.top.setStyle('position', 'absolute');
9646 this.maskEl.top.setStyle('z-index', zIndex);
9647 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9648 this.maskEl.top.setLeft(0);
9649 this.maskEl.top.setTop(0);
9650 this.maskEl.top.show();
9652 this.maskEl.left.setStyle('position', 'absolute');
9653 this.maskEl.left.setStyle('z-index', zIndex);
9654 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9655 this.maskEl.left.setLeft(0);
9656 this.maskEl.left.setTop(box.y - this.padding);
9657 this.maskEl.left.show();
9659 this.maskEl.bottom.setStyle('position', 'absolute');
9660 this.maskEl.bottom.setStyle('z-index', zIndex);
9661 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9662 this.maskEl.bottom.setLeft(0);
9663 this.maskEl.bottom.setTop(box.bottom + this.padding);
9664 this.maskEl.bottom.show();
9666 this.maskEl.right.setStyle('position', 'absolute');
9667 this.maskEl.right.setStyle('z-index', zIndex);
9668 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9669 this.maskEl.right.setLeft(box.right + this.padding);
9670 this.maskEl.right.setTop(box.y - this.padding);
9671 this.maskEl.right.show();
9673 this.toolTip.bindEl = this.target.el;
9675 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9677 var tip = this.target.blankText;
9679 if(this.target.getValue() !== '' ) {
9681 if (this.target.invalidText.length) {
9682 tip = this.target.invalidText;
9683 } else if (this.target.regexText.length){
9684 tip = this.target.regexText;
9688 this.toolTip.show(tip);
9690 this.intervalID = window.setInterval(function() {
9691 Roo.bootstrap.Form.popover.unmask();
9694 window.onwheel = function(){ return false;};
9696 (function(){ this.isMasked = true; }).defer(500, this);
9702 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9706 this.maskEl.top.setStyle('position', 'absolute');
9707 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9708 this.maskEl.top.hide();
9710 this.maskEl.left.setStyle('position', 'absolute');
9711 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9712 this.maskEl.left.hide();
9714 this.maskEl.bottom.setStyle('position', 'absolute');
9715 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9716 this.maskEl.bottom.hide();
9718 this.maskEl.right.setStyle('position', 'absolute');
9719 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9720 this.maskEl.right.hide();
9722 this.toolTip.hide();
9724 this.toolTip.el.hide();
9726 window.onwheel = function(){ return true;};
9728 if(this.intervalID){
9729 window.clearInterval(this.intervalID);
9730 this.intervalID = false;
9733 this.isMasked = false;
9743 * Ext JS Library 1.1.1
9744 * Copyright(c) 2006-2007, Ext JS, LLC.
9746 * Originally Released Under LGPL - original licence link has changed is not relivant.
9749 * <script type="text/javascript">
9752 * @class Roo.form.VTypes
9753 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9756 Roo.form.VTypes = function(){
9757 // closure these in so they are only created once.
9758 var alpha = /^[a-zA-Z_]+$/;
9759 var alphanum = /^[a-zA-Z0-9_]+$/;
9760 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9761 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9763 // All these messages and functions are configurable
9766 * The function used to validate email addresses
9767 * @param {String} value The email address
9769 'email' : function(v){
9770 return email.test(v);
9773 * The error text to display when the email validation function returns false
9776 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9778 * The keystroke filter mask to be applied on email input
9781 'emailMask' : /[a-z0-9_\.\-@]/i,
9784 * The function used to validate URLs
9785 * @param {String} value The URL
9787 'url' : function(v){
9791 * The error text to display when the url validation function returns false
9794 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9797 * The function used to validate alpha values
9798 * @param {String} value The value
9800 'alpha' : function(v){
9801 return alpha.test(v);
9804 * The error text to display when the alpha validation function returns false
9807 'alphaText' : 'This field should only contain letters and _',
9809 * The keystroke filter mask to be applied on alpha input
9812 'alphaMask' : /[a-z_]/i,
9815 * The function used to validate alphanumeric values
9816 * @param {String} value The value
9818 'alphanum' : function(v){
9819 return alphanum.test(v);
9822 * The error text to display when the alphanumeric validation function returns false
9825 'alphanumText' : 'This field should only contain letters, numbers and _',
9827 * The keystroke filter mask to be applied on alphanumeric input
9830 'alphanumMask' : /[a-z0-9_]/i
9840 * @class Roo.bootstrap.Input
9841 * @extends Roo.bootstrap.Component
9842 * Bootstrap Input class
9843 * @cfg {Boolean} disabled is it disabled
9844 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9845 * @cfg {String} name name of the input
9846 * @cfg {string} fieldLabel - the label associated
9847 * @cfg {string} placeholder - placeholder to put in text.
9848 * @cfg {string} before - input group add on before
9849 * @cfg {string} after - input group add on after
9850 * @cfg {string} size - (lg|sm) or leave empty..
9851 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9852 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9853 * @cfg {Number} md colspan out of 12 for computer-sized screens
9854 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9855 * @cfg {string} value default value of the input
9856 * @cfg {Number} labelWidth set the width of label
9857 * @cfg {Number} labellg set the width of label (1-12)
9858 * @cfg {Number} labelmd set the width of label (1-12)
9859 * @cfg {Number} labelsm set the width of label (1-12)
9860 * @cfg {Number} labelxs set the width of label (1-12)
9861 * @cfg {String} labelAlign (top|left)
9862 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9863 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9864 * @cfg {String} indicatorpos (left|right) default left
9865 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9866 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9868 * @cfg {String} align (left|center|right) Default left
9869 * @cfg {Boolean} forceFeedback (true|false) Default false
9872 * Create a new Input
9873 * @param {Object} config The config object
9876 Roo.bootstrap.Input = function(config){
9878 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9883 * Fires when this field receives input focus.
9884 * @param {Roo.form.Field} this
9889 * Fires when this field loses input focus.
9890 * @param {Roo.form.Field} this
9895 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9896 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9897 * @param {Roo.form.Field} this
9898 * @param {Roo.EventObject} e The event object
9903 * Fires just before the field blurs if the field value has changed.
9904 * @param {Roo.form.Field} this
9905 * @param {Mixed} newValue The new value
9906 * @param {Mixed} oldValue The original value
9911 * Fires after the field has been marked as invalid.
9912 * @param {Roo.form.Field} this
9913 * @param {String} msg The validation message
9918 * Fires after the field has been validated with no errors.
9919 * @param {Roo.form.Field} this
9924 * Fires after the key up
9925 * @param {Roo.form.Field} this
9926 * @param {Roo.EventObject} e The event Object
9932 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9934 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9935 automatic validation (defaults to "keyup").
9937 validationEvent : "keyup",
9939 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9941 validateOnBlur : true,
9943 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9945 validationDelay : 250,
9947 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9949 focusClass : "x-form-focus", // not needed???
9953 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9955 invalidClass : "has-warning",
9958 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9960 validClass : "has-success",
9963 * @cfg {Boolean} hasFeedback (true|false) default true
9968 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9970 invalidFeedbackClass : "glyphicon-warning-sign",
9973 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9975 validFeedbackClass : "glyphicon-ok",
9978 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9980 selectOnFocus : false,
9983 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9987 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9992 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9994 disableKeyFilter : false,
9997 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10001 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10005 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10007 blankText : "Please complete this mandatory field",
10010 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10014 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10016 maxLength : Number.MAX_VALUE,
10018 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10020 minLengthText : "The minimum length for this field is {0}",
10022 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10024 maxLengthText : "The maximum length for this field is {0}",
10028 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10029 * If available, this function will be called only after the basic validators all return true, and will be passed the
10030 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10034 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10035 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10036 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10040 * @cfg {String} regexText -- Depricated - use Invalid Text
10045 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10051 autocomplete: false,
10055 inputType : 'text',
10058 placeholder: false,
10063 preventMark: false,
10064 isFormField : true,
10067 labelAlign : false,
10070 formatedValue : false,
10071 forceFeedback : false,
10073 indicatorpos : 'left',
10083 parentLabelAlign : function()
10086 while (parent.parent()) {
10087 parent = parent.parent();
10088 if (typeof(parent.labelAlign) !='undefined') {
10089 return parent.labelAlign;
10096 getAutoCreate : function()
10098 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10104 if(this.inputType != 'hidden'){
10105 cfg.cls = 'form-group' //input-group
10111 type : this.inputType,
10112 value : this.value,
10113 cls : 'form-control',
10114 placeholder : this.placeholder || '',
10115 autocomplete : this.autocomplete || 'new-password'
10118 if(this.capture.length){
10119 input.capture = this.capture;
10122 if(this.accept.length){
10123 input.accept = this.accept + "/*";
10127 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10130 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10131 input.maxLength = this.maxLength;
10134 if (this.disabled) {
10135 input.disabled=true;
10138 if (this.readOnly) {
10139 input.readonly=true;
10143 input.name = this.name;
10147 input.cls += ' input-' + this.size;
10151 ['xs','sm','md','lg'].map(function(size){
10152 if (settings[size]) {
10153 cfg.cls += ' col-' + size + '-' + settings[size];
10157 var inputblock = input;
10161 cls: 'glyphicon form-control-feedback'
10164 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10167 cls : 'has-feedback',
10175 if (this.before || this.after) {
10178 cls : 'input-group',
10182 if (this.before && typeof(this.before) == 'string') {
10184 inputblock.cn.push({
10186 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10190 if (this.before && typeof(this.before) == 'object') {
10191 this.before = Roo.factory(this.before);
10193 inputblock.cn.push({
10195 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10196 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10200 inputblock.cn.push(input);
10202 if (this.after && typeof(this.after) == 'string') {
10203 inputblock.cn.push({
10205 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10209 if (this.after && typeof(this.after) == 'object') {
10210 this.after = Roo.factory(this.after);
10212 inputblock.cn.push({
10214 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10215 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10219 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10220 inputblock.cls += ' has-feedback';
10221 inputblock.cn.push(feedback);
10226 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10227 tooltip : 'This field is required'
10229 if (Roo.bootstrap.version == 4) {
10232 style : 'display-none'
10235 if (align ==='left' && this.fieldLabel.length) {
10237 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10244 cls : 'control-label col-form-label',
10245 html : this.fieldLabel
10256 var labelCfg = cfg.cn[1];
10257 var contentCfg = cfg.cn[2];
10259 if(this.indicatorpos == 'right'){
10264 cls : 'control-label col-form-label',
10268 html : this.fieldLabel
10282 labelCfg = cfg.cn[0];
10283 contentCfg = cfg.cn[1];
10287 if(this.labelWidth > 12){
10288 labelCfg.style = "width: " + this.labelWidth + 'px';
10291 if(this.labelWidth < 13 && this.labelmd == 0){
10292 this.labelmd = this.labelWidth;
10295 if(this.labellg > 0){
10296 labelCfg.cls += ' col-lg-' + this.labellg;
10297 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10300 if(this.labelmd > 0){
10301 labelCfg.cls += ' col-md-' + this.labelmd;
10302 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10305 if(this.labelsm > 0){
10306 labelCfg.cls += ' col-sm-' + this.labelsm;
10307 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10310 if(this.labelxs > 0){
10311 labelCfg.cls += ' col-xs-' + this.labelxs;
10312 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10316 } else if ( this.fieldLabel.length) {
10321 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10322 tooltip : 'This field is required'
10326 //cls : 'input-group-addon',
10327 html : this.fieldLabel
10335 if(this.indicatorpos == 'right'){
10340 //cls : 'input-group-addon',
10341 html : this.fieldLabel
10346 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10347 tooltip : 'This field is required'
10367 if (this.parentType === 'Navbar' && this.parent().bar) {
10368 cfg.cls += ' navbar-form';
10371 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10372 // on BS4 we do this only if not form
10373 cfg.cls += ' navbar-form';
10381 * return the real input element.
10383 inputEl: function ()
10385 return this.el.select('input.form-control',true).first();
10388 tooltipEl : function()
10390 return this.inputEl();
10393 indicatorEl : function()
10395 if (Roo.bootstrap.version == 4) {
10396 return false; // not enabled in v4 yet.
10399 var indicator = this.el.select('i.roo-required-indicator',true).first();
10409 setDisabled : function(v)
10411 var i = this.inputEl().dom;
10413 i.removeAttribute('disabled');
10417 i.setAttribute('disabled','true');
10419 initEvents : function()
10422 this.inputEl().on("keydown" , this.fireKey, this);
10423 this.inputEl().on("focus", this.onFocus, this);
10424 this.inputEl().on("blur", this.onBlur, this);
10426 this.inputEl().relayEvent('keyup', this);
10428 this.indicator = this.indicatorEl();
10430 if(this.indicator){
10431 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10434 // reference to original value for reset
10435 this.originalValue = this.getValue();
10436 //Roo.form.TextField.superclass.initEvents.call(this);
10437 if(this.validationEvent == 'keyup'){
10438 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10439 this.inputEl().on('keyup', this.filterValidation, this);
10441 else if(this.validationEvent !== false){
10442 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10445 if(this.selectOnFocus){
10446 this.on("focus", this.preFocus, this);
10449 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10450 this.inputEl().on("keypress", this.filterKeys, this);
10452 this.inputEl().relayEvent('keypress', this);
10455 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10456 this.el.on("click", this.autoSize, this);
10459 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10460 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10463 if (typeof(this.before) == 'object') {
10464 this.before.render(this.el.select('.roo-input-before',true).first());
10466 if (typeof(this.after) == 'object') {
10467 this.after.render(this.el.select('.roo-input-after',true).first());
10470 this.inputEl().on('change', this.onChange, this);
10473 filterValidation : function(e){
10474 if(!e.isNavKeyPress()){
10475 this.validationTask.delay(this.validationDelay);
10479 * Validates the field value
10480 * @return {Boolean} True if the value is valid, else false
10482 validate : function(){
10483 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10484 if(this.disabled || this.validateValue(this.getRawValue())){
10489 this.markInvalid();
10495 * Validates a value according to the field's validation rules and marks the field as invalid
10496 * if the validation fails
10497 * @param {Mixed} value The value to validate
10498 * @return {Boolean} True if the value is valid, else false
10500 validateValue : function(value)
10502 if(this.getVisibilityEl().hasClass('hidden')){
10506 if(value.length < 1) { // if it's blank
10507 if(this.allowBlank){
10513 if(value.length < this.minLength){
10516 if(value.length > this.maxLength){
10520 var vt = Roo.form.VTypes;
10521 if(!vt[this.vtype](value, this)){
10525 if(typeof this.validator == "function"){
10526 var msg = this.validator(value);
10530 if (typeof(msg) == 'string') {
10531 this.invalidText = msg;
10535 if(this.regex && !this.regex.test(value)){
10543 fireKey : function(e){
10544 //Roo.log('field ' + e.getKey());
10545 if(e.isNavKeyPress()){
10546 this.fireEvent("specialkey", this, e);
10549 focus : function (selectText){
10551 this.inputEl().focus();
10552 if(selectText === true){
10553 this.inputEl().dom.select();
10559 onFocus : function(){
10560 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10561 // this.el.addClass(this.focusClass);
10563 if(!this.hasFocus){
10564 this.hasFocus = true;
10565 this.startValue = this.getValue();
10566 this.fireEvent("focus", this);
10570 beforeBlur : Roo.emptyFn,
10574 onBlur : function(){
10576 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10577 //this.el.removeClass(this.focusClass);
10579 this.hasFocus = false;
10580 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10583 var v = this.getValue();
10584 if(String(v) !== String(this.startValue)){
10585 this.fireEvent('change', this, v, this.startValue);
10587 this.fireEvent("blur", this);
10590 onChange : function(e)
10592 var v = this.getValue();
10593 if(String(v) !== String(this.startValue)){
10594 this.fireEvent('change', this, v, this.startValue);
10600 * Resets the current field value to the originally loaded value and clears any validation messages
10602 reset : function(){
10603 this.setValue(this.originalValue);
10607 * Returns the name of the field
10608 * @return {Mixed} name The name field
10610 getName: function(){
10614 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10615 * @return {Mixed} value The field value
10617 getValue : function(){
10619 var v = this.inputEl().getValue();
10624 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10625 * @return {Mixed} value The field value
10627 getRawValue : function(){
10628 var v = this.inputEl().getValue();
10634 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10635 * @param {Mixed} value The value to set
10637 setRawValue : function(v){
10638 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10641 selectText : function(start, end){
10642 var v = this.getRawValue();
10644 start = start === undefined ? 0 : start;
10645 end = end === undefined ? v.length : end;
10646 var d = this.inputEl().dom;
10647 if(d.setSelectionRange){
10648 d.setSelectionRange(start, end);
10649 }else if(d.createTextRange){
10650 var range = d.createTextRange();
10651 range.moveStart("character", start);
10652 range.moveEnd("character", v.length-end);
10659 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10660 * @param {Mixed} value The value to set
10662 setValue : function(v){
10665 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10671 processValue : function(value){
10672 if(this.stripCharsRe){
10673 var newValue = value.replace(this.stripCharsRe, '');
10674 if(newValue !== value){
10675 this.setRawValue(newValue);
10682 preFocus : function(){
10684 if(this.selectOnFocus){
10685 this.inputEl().dom.select();
10688 filterKeys : function(e){
10689 var k = e.getKey();
10690 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10693 var c = e.getCharCode(), cc = String.fromCharCode(c);
10694 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10697 if(!this.maskRe.test(cc)){
10702 * Clear any invalid styles/messages for this field
10704 clearInvalid : function(){
10706 if(!this.el || this.preventMark){ // not rendered
10711 this.el.removeClass([this.invalidClass, 'is-invalid']);
10713 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10715 var feedback = this.el.select('.form-control-feedback', true).first();
10718 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10723 if(this.indicator){
10724 this.indicator.removeClass('visible');
10725 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10728 this.fireEvent('valid', this);
10732 * Mark this field as valid
10734 markValid : function()
10736 if(!this.el || this.preventMark){ // not rendered...
10740 this.el.removeClass([this.invalidClass, this.validClass]);
10741 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10743 var feedback = this.el.select('.form-control-feedback', true).first();
10746 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10749 if(this.indicator){
10750 this.indicator.removeClass('visible');
10751 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10758 if(this.allowBlank && !this.getRawValue().length){
10761 if (Roo.bootstrap.version == 3) {
10762 this.el.addClass(this.validClass);
10764 this.inputEl().addClass('is-valid');
10767 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10769 var feedback = this.el.select('.form-control-feedback', true).first();
10772 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10773 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10778 this.fireEvent('valid', this);
10782 * Mark this field as invalid
10783 * @param {String} msg The validation message
10785 markInvalid : function(msg)
10787 if(!this.el || this.preventMark){ // not rendered
10791 this.el.removeClass([this.invalidClass, this.validClass]);
10792 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10794 var feedback = this.el.select('.form-control-feedback', true).first();
10797 this.el.select('.form-control-feedback', true).first().removeClass(
10798 [this.invalidFeedbackClass, this.validFeedbackClass]);
10805 if(this.allowBlank && !this.getRawValue().length){
10809 if(this.indicator){
10810 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10811 this.indicator.addClass('visible');
10813 if (Roo.bootstrap.version == 3) {
10814 this.el.addClass(this.invalidClass);
10816 this.inputEl().addClass('is-invalid');
10821 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10823 var feedback = this.el.select('.form-control-feedback', true).first();
10826 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10828 if(this.getValue().length || this.forceFeedback){
10829 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10836 this.fireEvent('invalid', this, msg);
10839 SafariOnKeyDown : function(event)
10841 // this is a workaround for a password hang bug on chrome/ webkit.
10842 if (this.inputEl().dom.type != 'password') {
10846 var isSelectAll = false;
10848 if(this.inputEl().dom.selectionEnd > 0){
10849 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10851 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10852 event.preventDefault();
10857 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10859 event.preventDefault();
10860 // this is very hacky as keydown always get's upper case.
10862 var cc = String.fromCharCode(event.getCharCode());
10863 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10867 adjustWidth : function(tag, w){
10868 tag = tag.toLowerCase();
10869 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10870 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10871 if(tag == 'input'){
10874 if(tag == 'textarea'){
10877 }else if(Roo.isOpera){
10878 if(tag == 'input'){
10881 if(tag == 'textarea'){
10889 setFieldLabel : function(v)
10891 if(!this.rendered){
10895 if(this.indicatorEl()){
10896 var ar = this.el.select('label > span',true);
10898 if (ar.elements.length) {
10899 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10900 this.fieldLabel = v;
10904 var br = this.el.select('label',true);
10906 if(br.elements.length) {
10907 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10908 this.fieldLabel = v;
10912 Roo.log('Cannot Found any of label > span || label in input');
10916 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10917 this.fieldLabel = v;
10932 * @class Roo.bootstrap.TextArea
10933 * @extends Roo.bootstrap.Input
10934 * Bootstrap TextArea class
10935 * @cfg {Number} cols Specifies the visible width of a text area
10936 * @cfg {Number} rows Specifies the visible number of lines in a text area
10937 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10938 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10939 * @cfg {string} html text
10942 * Create a new TextArea
10943 * @param {Object} config The config object
10946 Roo.bootstrap.TextArea = function(config){
10947 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10951 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10961 getAutoCreate : function(){
10963 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10969 if(this.inputType != 'hidden'){
10970 cfg.cls = 'form-group' //input-group
10978 value : this.value || '',
10979 html: this.html || '',
10980 cls : 'form-control',
10981 placeholder : this.placeholder || ''
10985 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10986 input.maxLength = this.maxLength;
10990 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10994 input.cols = this.cols;
10997 if (this.readOnly) {
10998 input.readonly = true;
11002 input.name = this.name;
11006 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11010 ['xs','sm','md','lg'].map(function(size){
11011 if (settings[size]) {
11012 cfg.cls += ' col-' + size + '-' + settings[size];
11016 var inputblock = input;
11018 if(this.hasFeedback && !this.allowBlank){
11022 cls: 'glyphicon form-control-feedback'
11026 cls : 'has-feedback',
11035 if (this.before || this.after) {
11038 cls : 'input-group',
11042 inputblock.cn.push({
11044 cls : 'input-group-addon',
11049 inputblock.cn.push(input);
11051 if(this.hasFeedback && !this.allowBlank){
11052 inputblock.cls += ' has-feedback';
11053 inputblock.cn.push(feedback);
11057 inputblock.cn.push({
11059 cls : 'input-group-addon',
11066 if (align ==='left' && this.fieldLabel.length) {
11071 cls : 'control-label',
11072 html : this.fieldLabel
11083 if(this.labelWidth > 12){
11084 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11087 if(this.labelWidth < 13 && this.labelmd == 0){
11088 this.labelmd = this.labelWidth;
11091 if(this.labellg > 0){
11092 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11093 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11096 if(this.labelmd > 0){
11097 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11098 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11101 if(this.labelsm > 0){
11102 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11103 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11106 if(this.labelxs > 0){
11107 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11108 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11111 } else if ( this.fieldLabel.length) {
11116 //cls : 'input-group-addon',
11117 html : this.fieldLabel
11135 if (this.disabled) {
11136 input.disabled=true;
11143 * return the real textarea element.
11145 inputEl: function ()
11147 return this.el.select('textarea.form-control',true).first();
11151 * Clear any invalid styles/messages for this field
11153 clearInvalid : function()
11156 if(!this.el || this.preventMark){ // not rendered
11160 var label = this.el.select('label', true).first();
11161 var icon = this.el.select('i.fa-star', true).first();
11166 this.el.removeClass( this.validClass);
11167 this.inputEl().removeClass('is-invalid');
11169 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11171 var feedback = this.el.select('.form-control-feedback', true).first();
11174 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11179 this.fireEvent('valid', this);
11183 * Mark this field as valid
11185 markValid : function()
11187 if(!this.el || this.preventMark){ // not rendered
11191 this.el.removeClass([this.invalidClass, this.validClass]);
11192 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11194 var feedback = this.el.select('.form-control-feedback', true).first();
11197 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11200 if(this.disabled || this.allowBlank){
11204 var label = this.el.select('label', true).first();
11205 var icon = this.el.select('i.fa-star', true).first();
11210 if (Roo.bootstrap.version == 3) {
11211 this.el.addClass(this.validClass);
11213 this.inputEl().addClass('is-valid');
11217 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11219 var feedback = this.el.select('.form-control-feedback', true).first();
11222 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11223 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11228 this.fireEvent('valid', this);
11232 * Mark this field as invalid
11233 * @param {String} msg The validation message
11235 markInvalid : function(msg)
11237 if(!this.el || this.preventMark){ // not rendered
11241 this.el.removeClass([this.invalidClass, this.validClass]);
11242 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11244 var feedback = this.el.select('.form-control-feedback', true).first();
11247 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11250 if(this.disabled || this.allowBlank){
11254 var label = this.el.select('label', true).first();
11255 var icon = this.el.select('i.fa-star', true).first();
11257 if(!this.getValue().length && label && !icon){
11258 this.el.createChild({
11260 cls : 'text-danger fa fa-lg fa-star',
11261 tooltip : 'This field is required',
11262 style : 'margin-right:5px;'
11266 if (Roo.bootstrap.version == 3) {
11267 this.el.addClass(this.invalidClass);
11269 this.inputEl().addClass('is-invalid');
11272 // fixme ... this may be depricated need to test..
11273 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11275 var feedback = this.el.select('.form-control-feedback', true).first();
11278 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11280 if(this.getValue().length || this.forceFeedback){
11281 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11288 this.fireEvent('invalid', this, msg);
11296 * trigger field - base class for combo..
11301 * @class Roo.bootstrap.TriggerField
11302 * @extends Roo.bootstrap.Input
11303 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11304 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11305 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11306 * for which you can provide a custom implementation. For example:
11308 var trigger = new Roo.bootstrap.TriggerField();
11309 trigger.onTriggerClick = myTriggerFn;
11310 trigger.applyTo('my-field');
11313 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11314 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11315 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11316 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11317 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11320 * Create a new TriggerField.
11321 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11322 * to the base TextField)
11324 Roo.bootstrap.TriggerField = function(config){
11325 this.mimicing = false;
11326 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11329 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11331 * @cfg {String} triggerClass A CSS class to apply to the trigger
11334 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11339 * @cfg {Boolean} removable (true|false) special filter default false
11343 /** @cfg {Boolean} grow @hide */
11344 /** @cfg {Number} growMin @hide */
11345 /** @cfg {Number} growMax @hide */
11351 autoSize: Roo.emptyFn,
11355 deferHeight : true,
11358 actionMode : 'wrap',
11363 getAutoCreate : function(){
11365 var align = this.labelAlign || this.parentLabelAlign();
11370 cls: 'form-group' //input-group
11377 type : this.inputType,
11378 cls : 'form-control',
11379 autocomplete: 'new-password',
11380 placeholder : this.placeholder || ''
11384 input.name = this.name;
11387 input.cls += ' input-' + this.size;
11390 if (this.disabled) {
11391 input.disabled=true;
11394 var inputblock = input;
11396 if(this.hasFeedback && !this.allowBlank){
11400 cls: 'glyphicon form-control-feedback'
11403 if(this.removable && !this.editable && !this.tickable){
11405 cls : 'has-feedback',
11411 cls : 'roo-combo-removable-btn close'
11418 cls : 'has-feedback',
11427 if(this.removable && !this.editable && !this.tickable){
11429 cls : 'roo-removable',
11435 cls : 'roo-combo-removable-btn close'
11442 if (this.before || this.after) {
11445 cls : 'input-group',
11449 inputblock.cn.push({
11451 cls : 'input-group-addon input-group-prepend input-group-text',
11456 inputblock.cn.push(input);
11458 if(this.hasFeedback && !this.allowBlank){
11459 inputblock.cls += ' has-feedback';
11460 inputblock.cn.push(feedback);
11464 inputblock.cn.push({
11466 cls : 'input-group-addon input-group-append input-group-text',
11475 var ibwrap = inputblock;
11480 cls: 'roo-select2-choices',
11484 cls: 'roo-select2-search-field',
11496 cls: 'roo-select2-container input-group',
11501 cls: 'form-hidden-field'
11507 if(!this.multiple && this.showToggleBtn){
11513 if (this.caret != false) {
11516 cls: 'fa fa-' + this.caret
11523 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11525 Roo.bootstrap.version == 3 ? caret : '',
11528 cls: 'combobox-clear',
11542 combobox.cls += ' roo-select2-container-multi';
11546 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11547 tooltip : 'This field is required'
11549 if (Roo.bootstrap.version == 4) {
11552 style : 'display:none'
11557 if (align ==='left' && this.fieldLabel.length) {
11559 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11566 cls : 'control-label',
11567 html : this.fieldLabel
11579 var labelCfg = cfg.cn[1];
11580 var contentCfg = cfg.cn[2];
11582 if(this.indicatorpos == 'right'){
11587 cls : 'control-label',
11591 html : this.fieldLabel
11605 labelCfg = cfg.cn[0];
11606 contentCfg = cfg.cn[1];
11609 if(this.labelWidth > 12){
11610 labelCfg.style = "width: " + this.labelWidth + 'px';
11613 if(this.labelWidth < 13 && this.labelmd == 0){
11614 this.labelmd = this.labelWidth;
11617 if(this.labellg > 0){
11618 labelCfg.cls += ' col-lg-' + this.labellg;
11619 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11622 if(this.labelmd > 0){
11623 labelCfg.cls += ' col-md-' + this.labelmd;
11624 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11627 if(this.labelsm > 0){
11628 labelCfg.cls += ' col-sm-' + this.labelsm;
11629 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11632 if(this.labelxs > 0){
11633 labelCfg.cls += ' col-xs-' + this.labelxs;
11634 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11637 } else if ( this.fieldLabel.length) {
11638 // Roo.log(" label");
11643 //cls : 'input-group-addon',
11644 html : this.fieldLabel
11652 if(this.indicatorpos == 'right'){
11660 html : this.fieldLabel
11674 // Roo.log(" no label && no align");
11681 ['xs','sm','md','lg'].map(function(size){
11682 if (settings[size]) {
11683 cfg.cls += ' col-' + size + '-' + settings[size];
11694 onResize : function(w, h){
11695 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11696 // if(typeof w == 'number'){
11697 // var x = w - this.trigger.getWidth();
11698 // this.inputEl().setWidth(this.adjustWidth('input', x));
11699 // this.trigger.setStyle('left', x+'px');
11704 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11707 getResizeEl : function(){
11708 return this.inputEl();
11712 getPositionEl : function(){
11713 return this.inputEl();
11717 alignErrorIcon : function(){
11718 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11722 initEvents : function(){
11726 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11727 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11728 if(!this.multiple && this.showToggleBtn){
11729 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11730 if(this.hideTrigger){
11731 this.trigger.setDisplayed(false);
11733 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11737 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11740 if(this.removable && !this.editable && !this.tickable){
11741 var close = this.closeTriggerEl();
11744 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11745 close.on('click', this.removeBtnClick, this, close);
11749 //this.trigger.addClassOnOver('x-form-trigger-over');
11750 //this.trigger.addClassOnClick('x-form-trigger-click');
11753 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11757 closeTriggerEl : function()
11759 var close = this.el.select('.roo-combo-removable-btn', true).first();
11760 return close ? close : false;
11763 removeBtnClick : function(e, h, el)
11765 e.preventDefault();
11767 if(this.fireEvent("remove", this) !== false){
11769 this.fireEvent("afterremove", this)
11773 createList : function()
11775 this.list = Roo.get(document.body).createChild({
11776 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11777 cls: 'typeahead typeahead-long dropdown-menu',
11778 style: 'display:none'
11781 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11786 initTrigger : function(){
11791 onDestroy : function(){
11793 this.trigger.removeAllListeners();
11794 // this.trigger.remove();
11797 // this.wrap.remove();
11799 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11803 onFocus : function(){
11804 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11806 if(!this.mimicing){
11807 this.wrap.addClass('x-trigger-wrap-focus');
11808 this.mimicing = true;
11809 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11810 if(this.monitorTab){
11811 this.el.on("keydown", this.checkTab, this);
11818 checkTab : function(e){
11819 if(e.getKey() == e.TAB){
11820 this.triggerBlur();
11825 onBlur : function(){
11830 mimicBlur : function(e, t){
11832 if(!this.wrap.contains(t) && this.validateBlur()){
11833 this.triggerBlur();
11839 triggerBlur : function(){
11840 this.mimicing = false;
11841 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11842 if(this.monitorTab){
11843 this.el.un("keydown", this.checkTab, this);
11845 //this.wrap.removeClass('x-trigger-wrap-focus');
11846 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11850 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11851 validateBlur : function(e, t){
11856 onDisable : function(){
11857 this.inputEl().dom.disabled = true;
11858 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11860 // this.wrap.addClass('x-item-disabled');
11865 onEnable : function(){
11866 this.inputEl().dom.disabled = false;
11867 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11869 // this.el.removeClass('x-item-disabled');
11874 onShow : function(){
11875 var ae = this.getActionEl();
11878 ae.dom.style.display = '';
11879 ae.dom.style.visibility = 'visible';
11885 onHide : function(){
11886 var ae = this.getActionEl();
11887 ae.dom.style.display = 'none';
11891 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11892 * by an implementing function.
11894 * @param {EventObject} e
11896 onTriggerClick : Roo.emptyFn
11900 * Ext JS Library 1.1.1
11901 * Copyright(c) 2006-2007, Ext JS, LLC.
11903 * Originally Released Under LGPL - original licence link has changed is not relivant.
11906 * <script type="text/javascript">
11911 * @class Roo.data.SortTypes
11913 * Defines the default sorting (casting?) comparison functions used when sorting data.
11915 Roo.data.SortTypes = {
11917 * Default sort that does nothing
11918 * @param {Mixed} s The value being converted
11919 * @return {Mixed} The comparison value
11921 none : function(s){
11926 * The regular expression used to strip tags
11930 stripTagsRE : /<\/?[^>]+>/gi,
11933 * Strips all HTML tags to sort on text only
11934 * @param {Mixed} s The value being converted
11935 * @return {String} The comparison value
11937 asText : function(s){
11938 return String(s).replace(this.stripTagsRE, "");
11942 * Strips all HTML tags to sort on text only - Case insensitive
11943 * @param {Mixed} s The value being converted
11944 * @return {String} The comparison value
11946 asUCText : function(s){
11947 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11951 * Case insensitive string
11952 * @param {Mixed} s The value being converted
11953 * @return {String} The comparison value
11955 asUCString : function(s) {
11956 return String(s).toUpperCase();
11961 * @param {Mixed} s The value being converted
11962 * @return {Number} The comparison value
11964 asDate : function(s) {
11968 if(s instanceof Date){
11969 return s.getTime();
11971 return Date.parse(String(s));
11976 * @param {Mixed} s The value being converted
11977 * @return {Float} The comparison value
11979 asFloat : function(s) {
11980 var val = parseFloat(String(s).replace(/,/g, ""));
11989 * @param {Mixed} s The value being converted
11990 * @return {Number} The comparison value
11992 asInt : function(s) {
11993 var val = parseInt(String(s).replace(/,/g, ""));
12001 * Ext JS Library 1.1.1
12002 * Copyright(c) 2006-2007, Ext JS, LLC.
12004 * Originally Released Under LGPL - original licence link has changed is not relivant.
12007 * <script type="text/javascript">
12011 * @class Roo.data.Record
12012 * Instances of this class encapsulate both record <em>definition</em> information, and record
12013 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12014 * to access Records cached in an {@link Roo.data.Store} object.<br>
12016 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12017 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12020 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12022 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12023 * {@link #create}. The parameters are the same.
12024 * @param {Array} data An associative Array of data values keyed by the field name.
12025 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12026 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12027 * not specified an integer id is generated.
12029 Roo.data.Record = function(data, id){
12030 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12035 * Generate a constructor for a specific record layout.
12036 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12037 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12038 * Each field definition object may contain the following properties: <ul>
12039 * <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,
12040 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12041 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12042 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12043 * is being used, then this is a string containing the javascript expression to reference the data relative to
12044 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12045 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12046 * this may be omitted.</p></li>
12047 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12048 * <ul><li>auto (Default, implies no conversion)</li>
12053 * <li>date</li></ul></p></li>
12054 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12055 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12056 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12057 * by the Reader into an object that will be stored in the Record. It is passed the
12058 * following parameters:<ul>
12059 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12061 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12063 * <br>usage:<br><pre><code>
12064 var TopicRecord = Roo.data.Record.create(
12065 {name: 'title', mapping: 'topic_title'},
12066 {name: 'author', mapping: 'username'},
12067 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12068 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12069 {name: 'lastPoster', mapping: 'user2'},
12070 {name: 'excerpt', mapping: 'post_text'}
12073 var myNewRecord = new TopicRecord({
12074 title: 'Do my job please',
12077 lastPost: new Date(),
12078 lastPoster: 'Animal',
12079 excerpt: 'No way dude!'
12081 myStore.add(myNewRecord);
12086 Roo.data.Record.create = function(o){
12087 var f = function(){
12088 f.superclass.constructor.apply(this, arguments);
12090 Roo.extend(f, Roo.data.Record);
12091 var p = f.prototype;
12092 p.fields = new Roo.util.MixedCollection(false, function(field){
12095 for(var i = 0, len = o.length; i < len; i++){
12096 p.fields.add(new Roo.data.Field(o[i]));
12098 f.getField = function(name){
12099 return p.fields.get(name);
12104 Roo.data.Record.AUTO_ID = 1000;
12105 Roo.data.Record.EDIT = 'edit';
12106 Roo.data.Record.REJECT = 'reject';
12107 Roo.data.Record.COMMIT = 'commit';
12109 Roo.data.Record.prototype = {
12111 * Readonly flag - true if this record has been modified.
12120 join : function(store){
12121 this.store = store;
12125 * Set the named field to the specified value.
12126 * @param {String} name The name of the field to set.
12127 * @param {Object} value The value to set the field to.
12129 set : function(name, value){
12130 if(this.data[name] == value){
12134 if(!this.modified){
12135 this.modified = {};
12137 if(typeof this.modified[name] == 'undefined'){
12138 this.modified[name] = this.data[name];
12140 this.data[name] = value;
12141 if(!this.editing && this.store){
12142 this.store.afterEdit(this);
12147 * Get the value of the named field.
12148 * @param {String} name The name of the field to get the value of.
12149 * @return {Object} The value of the field.
12151 get : function(name){
12152 return this.data[name];
12156 beginEdit : function(){
12157 this.editing = true;
12158 this.modified = {};
12162 cancelEdit : function(){
12163 this.editing = false;
12164 delete this.modified;
12168 endEdit : function(){
12169 this.editing = false;
12170 if(this.dirty && this.store){
12171 this.store.afterEdit(this);
12176 * Usually called by the {@link Roo.data.Store} which owns the Record.
12177 * Rejects all changes made to the Record since either creation, or the last commit operation.
12178 * Modified fields are reverted to their original values.
12180 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12181 * of reject operations.
12183 reject : function(){
12184 var m = this.modified;
12186 if(typeof m[n] != "function"){
12187 this.data[n] = m[n];
12190 this.dirty = false;
12191 delete this.modified;
12192 this.editing = false;
12194 this.store.afterReject(this);
12199 * Usually called by the {@link Roo.data.Store} which owns the Record.
12200 * Commits all changes made to the Record since either creation, or the last commit operation.
12202 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12203 * of commit operations.
12205 commit : function(){
12206 this.dirty = false;
12207 delete this.modified;
12208 this.editing = false;
12210 this.store.afterCommit(this);
12215 hasError : function(){
12216 return this.error != null;
12220 clearError : function(){
12225 * Creates a copy of this record.
12226 * @param {String} id (optional) A new record id if you don't want to use this record's id
12229 copy : function(newId) {
12230 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12234 * Ext JS Library 1.1.1
12235 * Copyright(c) 2006-2007, Ext JS, LLC.
12237 * Originally Released Under LGPL - original licence link has changed is not relivant.
12240 * <script type="text/javascript">
12246 * @class Roo.data.Store
12247 * @extends Roo.util.Observable
12248 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12249 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12251 * 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
12252 * has no knowledge of the format of the data returned by the Proxy.<br>
12254 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12255 * instances from the data object. These records are cached and made available through accessor functions.
12257 * Creates a new Store.
12258 * @param {Object} config A config object containing the objects needed for the Store to access data,
12259 * and read the data into Records.
12261 Roo.data.Store = function(config){
12262 this.data = new Roo.util.MixedCollection(false);
12263 this.data.getKey = function(o){
12266 this.baseParams = {};
12268 this.paramNames = {
12273 "multisort" : "_multisort"
12276 if(config && config.data){
12277 this.inlineData = config.data;
12278 delete config.data;
12281 Roo.apply(this, config);
12283 if(this.reader){ // reader passed
12284 this.reader = Roo.factory(this.reader, Roo.data);
12285 this.reader.xmodule = this.xmodule || false;
12286 if(!this.recordType){
12287 this.recordType = this.reader.recordType;
12289 if(this.reader.onMetaChange){
12290 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12294 if(this.recordType){
12295 this.fields = this.recordType.prototype.fields;
12297 this.modified = [];
12301 * @event datachanged
12302 * Fires when the data cache has changed, and a widget which is using this Store
12303 * as a Record cache should refresh its view.
12304 * @param {Store} this
12306 datachanged : true,
12308 * @event metachange
12309 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12310 * @param {Store} this
12311 * @param {Object} meta The JSON metadata
12316 * Fires when Records have been added to the Store
12317 * @param {Store} this
12318 * @param {Roo.data.Record[]} records The array of Records added
12319 * @param {Number} index The index at which the record(s) were added
12324 * Fires when a Record has been removed from the Store
12325 * @param {Store} this
12326 * @param {Roo.data.Record} record The Record that was removed
12327 * @param {Number} index The index at which the record was removed
12332 * Fires when a Record has been updated
12333 * @param {Store} this
12334 * @param {Roo.data.Record} record The Record that was updated
12335 * @param {String} operation The update operation being performed. Value may be one of:
12337 Roo.data.Record.EDIT
12338 Roo.data.Record.REJECT
12339 Roo.data.Record.COMMIT
12345 * Fires when the data cache has been cleared.
12346 * @param {Store} this
12350 * @event beforeload
12351 * Fires before a request is made for a new data object. If the beforeload handler returns false
12352 * the load action will be canceled.
12353 * @param {Store} this
12354 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12358 * @event beforeloadadd
12359 * Fires after a new set of Records has been loaded.
12360 * @param {Store} this
12361 * @param {Roo.data.Record[]} records The Records that were loaded
12362 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12364 beforeloadadd : true,
12367 * Fires after a new set of Records has been loaded, before they are added to the store.
12368 * @param {Store} this
12369 * @param {Roo.data.Record[]} records The Records that were loaded
12370 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12371 * @params {Object} return from reader
12375 * @event loadexception
12376 * Fires if an exception occurs in the Proxy during loading.
12377 * Called with the signature of the Proxy's "loadexception" event.
12378 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12381 * @param {Object} return from JsonData.reader() - success, totalRecords, records
12382 * @param {Object} load options
12383 * @param {Object} jsonData from your request (normally this contains the Exception)
12385 loadexception : true
12389 this.proxy = Roo.factory(this.proxy, Roo.data);
12390 this.proxy.xmodule = this.xmodule || false;
12391 this.relayEvents(this.proxy, ["loadexception"]);
12393 this.sortToggle = {};
12394 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12396 Roo.data.Store.superclass.constructor.call(this);
12398 if(this.inlineData){
12399 this.loadData(this.inlineData);
12400 delete this.inlineData;
12404 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12406 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
12407 * without a remote query - used by combo/forms at present.
12411 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12414 * @cfg {Array} data Inline data to be loaded when the store is initialized.
12417 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12418 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12421 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12422 * on any HTTP request
12425 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12428 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12432 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12433 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12435 remoteSort : false,
12438 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12439 * loaded or when a record is removed. (defaults to false).
12441 pruneModifiedRecords : false,
12444 lastOptions : null,
12447 * Add Records to the Store and fires the add event.
12448 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12450 add : function(records){
12451 records = [].concat(records);
12452 for(var i = 0, len = records.length; i < len; i++){
12453 records[i].join(this);
12455 var index = this.data.length;
12456 this.data.addAll(records);
12457 this.fireEvent("add", this, records, index);
12461 * Remove a Record from the Store and fires the remove event.
12462 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12464 remove : function(record){
12465 var index = this.data.indexOf(record);
12466 this.data.removeAt(index);
12468 if(this.pruneModifiedRecords){
12469 this.modified.remove(record);
12471 this.fireEvent("remove", this, record, index);
12475 * Remove all Records from the Store and fires the clear event.
12477 removeAll : function(){
12479 if(this.pruneModifiedRecords){
12480 this.modified = [];
12482 this.fireEvent("clear", this);
12486 * Inserts Records to the Store at the given index and fires the add event.
12487 * @param {Number} index The start index at which to insert the passed Records.
12488 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12490 insert : function(index, records){
12491 records = [].concat(records);
12492 for(var i = 0, len = records.length; i < len; i++){
12493 this.data.insert(index, records[i]);
12494 records[i].join(this);
12496 this.fireEvent("add", this, records, index);
12500 * Get the index within the cache of the passed Record.
12501 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12502 * @return {Number} The index of the passed Record. Returns -1 if not found.
12504 indexOf : function(record){
12505 return this.data.indexOf(record);
12509 * Get the index within the cache of the Record with the passed id.
12510 * @param {String} id The id of the Record to find.
12511 * @return {Number} The index of the Record. Returns -1 if not found.
12513 indexOfId : function(id){
12514 return this.data.indexOfKey(id);
12518 * Get the Record with the specified id.
12519 * @param {String} id The id of the Record to find.
12520 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12522 getById : function(id){
12523 return this.data.key(id);
12527 * Get the Record at the specified index.
12528 * @param {Number} index The index of the Record to find.
12529 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12531 getAt : function(index){
12532 return this.data.itemAt(index);
12536 * Returns a range of Records between specified indices.
12537 * @param {Number} startIndex (optional) The starting index (defaults to 0)
12538 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12539 * @return {Roo.data.Record[]} An array of Records
12541 getRange : function(start, end){
12542 return this.data.getRange(start, end);
12546 storeOptions : function(o){
12547 o = Roo.apply({}, o);
12550 this.lastOptions = o;
12554 * Loads the Record cache from the configured Proxy using the configured Reader.
12556 * If using remote paging, then the first load call must specify the <em>start</em>
12557 * and <em>limit</em> properties in the options.params property to establish the initial
12558 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12560 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12561 * and this call will return before the new data has been loaded. Perform any post-processing
12562 * in a callback function, or in a "load" event handler.</strong>
12564 * @param {Object} options An object containing properties which control loading options:<ul>
12565 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12566 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12567 * passed the following arguments:<ul>
12568 * <li>r : Roo.data.Record[]</li>
12569 * <li>options: Options object from the load call</li>
12570 * <li>success: Boolean success indicator</li></ul></li>
12571 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12572 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12575 load : function(options){
12576 options = options || {};
12577 if(this.fireEvent("beforeload", this, options) !== false){
12578 this.storeOptions(options);
12579 var p = Roo.apply(options.params || {}, this.baseParams);
12580 // if meta was not loaded from remote source.. try requesting it.
12581 if (!this.reader.metaFromRemote) {
12582 p._requestMeta = 1;
12584 if(this.sortInfo && this.remoteSort){
12585 var pn = this.paramNames;
12586 p[pn["sort"]] = this.sortInfo.field;
12587 p[pn["dir"]] = this.sortInfo.direction;
12589 if (this.multiSort) {
12590 var pn = this.paramNames;
12591 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12594 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12599 * Reloads the Record cache from the configured Proxy using the configured Reader and
12600 * the options from the last load operation performed.
12601 * @param {Object} options (optional) An object containing properties which may override the options
12602 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12603 * the most recently used options are reused).
12605 reload : function(options){
12606 this.load(Roo.applyIf(options||{}, this.lastOptions));
12610 // Called as a callback by the Reader during a load operation.
12611 loadRecords : function(o, options, success){
12612 if(!o || success === false){
12613 if(success !== false){
12614 this.fireEvent("load", this, [], options, o);
12616 if(options.callback){
12617 options.callback.call(options.scope || this, [], options, false);
12621 // if data returned failure - throw an exception.
12622 if (o.success === false) {
12623 // show a message if no listener is registered.
12624 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12625 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12627 // loadmask wil be hooked into this..
12628 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12631 var r = o.records, t = o.totalRecords || r.length;
12633 this.fireEvent("beforeloadadd", this, r, options, o);
12635 if(!options || options.add !== true){
12636 if(this.pruneModifiedRecords){
12637 this.modified = [];
12639 for(var i = 0, len = r.length; i < len; i++){
12643 this.data = this.snapshot;
12644 delete this.snapshot;
12647 this.data.addAll(r);
12648 this.totalLength = t;
12650 this.fireEvent("datachanged", this);
12652 this.totalLength = Math.max(t, this.data.length+r.length);
12656 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12658 var e = new Roo.data.Record({});
12660 e.set(this.parent.displayField, this.parent.emptyTitle);
12661 e.set(this.parent.valueField, '');
12666 this.fireEvent("load", this, r, options, o);
12667 if(options.callback){
12668 options.callback.call(options.scope || this, r, options, true);
12674 * Loads data from a passed data block. A Reader which understands the format of the data
12675 * must have been configured in the constructor.
12676 * @param {Object} data The data block from which to read the Records. The format of the data expected
12677 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12678 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12680 loadData : function(o, append){
12681 var r = this.reader.readRecords(o);
12682 this.loadRecords(r, {add: append}, true);
12686 * using 'cn' the nested child reader read the child array into it's child stores.
12687 * @param {Object} rec The record with a 'children array
12689 loadDataFromChildren : function(rec)
12691 this.loadData(this.reader.toLoadData(rec));
12696 * Gets the number of cached records.
12698 * <em>If using paging, this may not be the total size of the dataset. If the data object
12699 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12700 * the data set size</em>
12702 getCount : function(){
12703 return this.data.length || 0;
12707 * Gets the total number of records in the dataset as returned by the server.
12709 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12710 * the dataset size</em>
12712 getTotalCount : function(){
12713 return this.totalLength || 0;
12717 * Returns the sort state of the Store as an object with two properties:
12719 field {String} The name of the field by which the Records are sorted
12720 direction {String} The sort order, "ASC" or "DESC"
12723 getSortState : function(){
12724 return this.sortInfo;
12728 applySort : function(){
12729 if(this.sortInfo && !this.remoteSort){
12730 var s = this.sortInfo, f = s.field;
12731 var st = this.fields.get(f).sortType;
12732 var fn = function(r1, r2){
12733 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12734 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12736 this.data.sort(s.direction, fn);
12737 if(this.snapshot && this.snapshot != this.data){
12738 this.snapshot.sort(s.direction, fn);
12744 * Sets the default sort column and order to be used by the next load operation.
12745 * @param {String} fieldName The name of the field to sort by.
12746 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12748 setDefaultSort : function(field, dir){
12749 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12753 * Sort the Records.
12754 * If remote sorting is used, the sort is performed on the server, and the cache is
12755 * reloaded. If local sorting is used, the cache is sorted internally.
12756 * @param {String} fieldName The name of the field to sort by.
12757 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12759 sort : function(fieldName, dir){
12760 var f = this.fields.get(fieldName);
12762 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12764 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12765 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12770 this.sortToggle[f.name] = dir;
12771 this.sortInfo = {field: f.name, direction: dir};
12772 if(!this.remoteSort){
12774 this.fireEvent("datachanged", this);
12776 this.load(this.lastOptions);
12781 * Calls the specified function for each of the Records in the cache.
12782 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12783 * Returning <em>false</em> aborts and exits the iteration.
12784 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12786 each : function(fn, scope){
12787 this.data.each(fn, scope);
12791 * Gets all records modified since the last commit. Modified records are persisted across load operations
12792 * (e.g., during paging).
12793 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12795 getModifiedRecords : function(){
12796 return this.modified;
12800 createFilterFn : function(property, value, anyMatch){
12801 if(!value.exec){ // not a regex
12802 value = String(value);
12803 if(value.length == 0){
12806 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12808 return function(r){
12809 return value.test(r.data[property]);
12814 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12815 * @param {String} property A field on your records
12816 * @param {Number} start The record index to start at (defaults to 0)
12817 * @param {Number} end The last record index to include (defaults to length - 1)
12818 * @return {Number} The sum
12820 sum : function(property, start, end){
12821 var rs = this.data.items, v = 0;
12822 start = start || 0;
12823 end = (end || end === 0) ? end : rs.length-1;
12825 for(var i = start; i <= end; i++){
12826 v += (rs[i].data[property] || 0);
12832 * Filter the records by a specified property.
12833 * @param {String} field A field on your records
12834 * @param {String/RegExp} value Either a string that the field
12835 * should start with or a RegExp to test against the field
12836 * @param {Boolean} anyMatch True to match any part not just the beginning
12838 filter : function(property, value, anyMatch){
12839 var fn = this.createFilterFn(property, value, anyMatch);
12840 return fn ? this.filterBy(fn) : this.clearFilter();
12844 * Filter by a function. The specified function will be called with each
12845 * record in this data source. If the function returns true the record is included,
12846 * otherwise it is filtered.
12847 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12848 * @param {Object} scope (optional) The scope of the function (defaults to this)
12850 filterBy : function(fn, scope){
12851 this.snapshot = this.snapshot || this.data;
12852 this.data = this.queryBy(fn, scope||this);
12853 this.fireEvent("datachanged", this);
12857 * Query the records by a specified property.
12858 * @param {String} field A field on your records
12859 * @param {String/RegExp} value Either a string that the field
12860 * should start with or a RegExp to test against the field
12861 * @param {Boolean} anyMatch True to match any part not just the beginning
12862 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12864 query : function(property, value, anyMatch){
12865 var fn = this.createFilterFn(property, value, anyMatch);
12866 return fn ? this.queryBy(fn) : this.data.clone();
12870 * Query by a function. The specified function will be called with each
12871 * record in this data source. If the function returns true the record is included
12873 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12874 * @param {Object} scope (optional) The scope of the function (defaults to this)
12875 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12877 queryBy : function(fn, scope){
12878 var data = this.snapshot || this.data;
12879 return data.filterBy(fn, scope||this);
12883 * Collects unique values for a particular dataIndex from this store.
12884 * @param {String} dataIndex The property to collect
12885 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12886 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12887 * @return {Array} An array of the unique values
12889 collect : function(dataIndex, allowNull, bypassFilter){
12890 var d = (bypassFilter === true && this.snapshot) ?
12891 this.snapshot.items : this.data.items;
12892 var v, sv, r = [], l = {};
12893 for(var i = 0, len = d.length; i < len; i++){
12894 v = d[i].data[dataIndex];
12896 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12905 * Revert to a view of the Record cache with no filtering applied.
12906 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12908 clearFilter : function(suppressEvent){
12909 if(this.snapshot && this.snapshot != this.data){
12910 this.data = this.snapshot;
12911 delete this.snapshot;
12912 if(suppressEvent !== true){
12913 this.fireEvent("datachanged", this);
12919 afterEdit : function(record){
12920 if(this.modified.indexOf(record) == -1){
12921 this.modified.push(record);
12923 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12927 afterReject : function(record){
12928 this.modified.remove(record);
12929 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12933 afterCommit : function(record){
12934 this.modified.remove(record);
12935 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12939 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12940 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12942 commitChanges : function(){
12943 var m = this.modified.slice(0);
12944 this.modified = [];
12945 for(var i = 0, len = m.length; i < len; i++){
12951 * Cancel outstanding changes on all changed records.
12953 rejectChanges : function(){
12954 var m = this.modified.slice(0);
12955 this.modified = [];
12956 for(var i = 0, len = m.length; i < len; i++){
12961 onMetaChange : function(meta, rtype, o){
12962 this.recordType = rtype;
12963 this.fields = rtype.prototype.fields;
12964 delete this.snapshot;
12965 this.sortInfo = meta.sortInfo || this.sortInfo;
12966 this.modified = [];
12967 this.fireEvent('metachange', this, this.reader.meta);
12970 moveIndex : function(data, type)
12972 var index = this.indexOf(data);
12974 var newIndex = index + type;
12978 this.insert(newIndex, data);
12983 * Ext JS Library 1.1.1
12984 * Copyright(c) 2006-2007, Ext JS, LLC.
12986 * Originally Released Under LGPL - original licence link has changed is not relivant.
12989 * <script type="text/javascript">
12993 * @class Roo.data.SimpleStore
12994 * @extends Roo.data.Store
12995 * Small helper class to make creating Stores from Array data easier.
12996 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12997 * @cfg {Array} fields An array of field definition objects, or field name strings.
12998 * @cfg {Object} an existing reader (eg. copied from another store)
12999 * @cfg {Array} data The multi-dimensional array of data
13001 * @param {Object} config
13003 Roo.data.SimpleStore = function(config)
13005 Roo.data.SimpleStore.superclass.constructor.call(this, {
13007 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13010 Roo.data.Record.create(config.fields)
13012 proxy : new Roo.data.MemoryProxy(config.data)
13016 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13018 * Ext JS Library 1.1.1
13019 * Copyright(c) 2006-2007, Ext JS, LLC.
13021 * Originally Released Under LGPL - original licence link has changed is not relivant.
13024 * <script type="text/javascript">
13029 * @extends Roo.data.Store
13030 * @class Roo.data.JsonStore
13031 * Small helper class to make creating Stores for JSON data easier. <br/>
13033 var store = new Roo.data.JsonStore({
13034 url: 'get-images.php',
13036 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13039 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13040 * JsonReader and HttpProxy (unless inline data is provided).</b>
13041 * @cfg {Array} fields An array of field definition objects, or field name strings.
13043 * @param {Object} config
13045 Roo.data.JsonStore = function(c){
13046 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13047 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13048 reader: new Roo.data.JsonReader(c, c.fields)
13051 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13053 * Ext JS Library 1.1.1
13054 * Copyright(c) 2006-2007, Ext JS, LLC.
13056 * Originally Released Under LGPL - original licence link has changed is not relivant.
13059 * <script type="text/javascript">
13063 Roo.data.Field = function(config){
13064 if(typeof config == "string"){
13065 config = {name: config};
13067 Roo.apply(this, config);
13070 this.type = "auto";
13073 var st = Roo.data.SortTypes;
13074 // named sortTypes are supported, here we look them up
13075 if(typeof this.sortType == "string"){
13076 this.sortType = st[this.sortType];
13079 // set default sortType for strings and dates
13080 if(!this.sortType){
13083 this.sortType = st.asUCString;
13086 this.sortType = st.asDate;
13089 this.sortType = st.none;
13094 var stripRe = /[\$,%]/g;
13096 // prebuilt conversion function for this field, instead of
13097 // switching every time we're reading a value
13099 var cv, dateFormat = this.dateFormat;
13104 cv = function(v){ return v; };
13107 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13111 return v !== undefined && v !== null && v !== '' ?
13112 parseInt(String(v).replace(stripRe, ""), 10) : '';
13117 return v !== undefined && v !== null && v !== '' ?
13118 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13123 cv = function(v){ return v === true || v === "true" || v == 1; };
13130 if(v instanceof Date){
13134 if(dateFormat == "timestamp"){
13135 return new Date(v*1000);
13137 return Date.parseDate(v, dateFormat);
13139 var parsed = Date.parse(v);
13140 return parsed ? new Date(parsed) : null;
13149 Roo.data.Field.prototype = {
13157 * Ext JS Library 1.1.1
13158 * Copyright(c) 2006-2007, Ext JS, LLC.
13160 * Originally Released Under LGPL - original licence link has changed is not relivant.
13163 * <script type="text/javascript">
13166 // Base class for reading structured data from a data source. This class is intended to be
13167 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13170 * @class Roo.data.DataReader
13171 * Base class for reading structured data from a data source. This class is intended to be
13172 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13175 Roo.data.DataReader = function(meta, recordType){
13179 this.recordType = recordType instanceof Array ?
13180 Roo.data.Record.create(recordType) : recordType;
13183 Roo.data.DataReader.prototype = {
13186 readerType : 'Data',
13188 * Create an empty record
13189 * @param {Object} data (optional) - overlay some values
13190 * @return {Roo.data.Record} record created.
13192 newRow : function(d) {
13194 this.recordType.prototype.fields.each(function(c) {
13196 case 'int' : da[c.name] = 0; break;
13197 case 'date' : da[c.name] = new Date(); break;
13198 case 'float' : da[c.name] = 0.0; break;
13199 case 'boolean' : da[c.name] = false; break;
13200 default : da[c.name] = ""; break;
13204 return new this.recordType(Roo.apply(da, d));
13210 * Ext JS Library 1.1.1
13211 * Copyright(c) 2006-2007, Ext JS, LLC.
13213 * Originally Released Under LGPL - original licence link has changed is not relivant.
13216 * <script type="text/javascript">
13220 * @class Roo.data.DataProxy
13221 * @extends Roo.data.Observable
13222 * This class is an abstract base class for implementations which provide retrieval of
13223 * unformatted data objects.<br>
13225 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13226 * (of the appropriate type which knows how to parse the data object) to provide a block of
13227 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13229 * Custom implementations must implement the load method as described in
13230 * {@link Roo.data.HttpProxy#load}.
13232 Roo.data.DataProxy = function(){
13235 * @event beforeload
13236 * Fires before a network request is made to retrieve a data object.
13237 * @param {Object} This DataProxy object.
13238 * @param {Object} params The params parameter to the load function.
13243 * Fires before the load method's callback is called.
13244 * @param {Object} This DataProxy object.
13245 * @param {Object} o The data object.
13246 * @param {Object} arg The callback argument object passed to the load function.
13250 * @event loadexception
13251 * Fires if an Exception occurs during data retrieval.
13252 * @param {Object} This DataProxy object.
13253 * @param {Object} o The data object.
13254 * @param {Object} arg The callback argument object passed to the load function.
13255 * @param {Object} e The Exception.
13257 loadexception : true
13259 Roo.data.DataProxy.superclass.constructor.call(this);
13262 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13265 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13269 * Ext JS Library 1.1.1
13270 * Copyright(c) 2006-2007, Ext JS, LLC.
13272 * Originally Released Under LGPL - original licence link has changed is not relivant.
13275 * <script type="text/javascript">
13278 * @class Roo.data.MemoryProxy
13279 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13280 * to the Reader when its load method is called.
13282 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13284 Roo.data.MemoryProxy = function(data){
13288 Roo.data.MemoryProxy.superclass.constructor.call(this);
13292 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13295 * Load data from the requested source (in this case an in-memory
13296 * data object passed to the constructor), read the data object into
13297 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13298 * process that block using the passed callback.
13299 * @param {Object} params This parameter is not used by the MemoryProxy class.
13300 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13301 * object into a block of Roo.data.Records.
13302 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13303 * The function must be passed <ul>
13304 * <li>The Record block object</li>
13305 * <li>The "arg" argument from the load function</li>
13306 * <li>A boolean success indicator</li>
13308 * @param {Object} scope The scope in which to call the callback
13309 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13311 load : function(params, reader, callback, scope, arg){
13312 params = params || {};
13315 result = reader.readRecords(params.data ? params.data :this.data);
13317 this.fireEvent("loadexception", this, arg, null, e);
13318 callback.call(scope, null, arg, false);
13321 callback.call(scope, result, arg, true);
13325 update : function(params, records){
13330 * Ext JS Library 1.1.1
13331 * Copyright(c) 2006-2007, Ext JS, LLC.
13333 * Originally Released Under LGPL - original licence link has changed is not relivant.
13336 * <script type="text/javascript">
13339 * @class Roo.data.HttpProxy
13340 * @extends Roo.data.DataProxy
13341 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13342 * configured to reference a certain URL.<br><br>
13344 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13345 * from which the running page was served.<br><br>
13347 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13349 * Be aware that to enable the browser to parse an XML document, the server must set
13350 * the Content-Type header in the HTTP response to "text/xml".
13352 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13353 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
13354 * will be used to make the request.
13356 Roo.data.HttpProxy = function(conn){
13357 Roo.data.HttpProxy.superclass.constructor.call(this);
13358 // is conn a conn config or a real conn?
13360 this.useAjax = !conn || !conn.events;
13364 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13365 // thse are take from connection...
13368 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13371 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13372 * extra parameters to each request made by this object. (defaults to undefined)
13375 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13376 * to each request made by this object. (defaults to undefined)
13379 * @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)
13382 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13385 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13391 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13395 * Return the {@link Roo.data.Connection} object being used by this Proxy.
13396 * @return {Connection} The Connection object. This object may be used to subscribe to events on
13397 * a finer-grained basis than the DataProxy events.
13399 getConnection : function(){
13400 return this.useAjax ? Roo.Ajax : this.conn;
13404 * Load data from the configured {@link Roo.data.Connection}, read the data object into
13405 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13406 * process that block using the passed callback.
13407 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13408 * for the request to the remote server.
13409 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13410 * object into a block of Roo.data.Records.
13411 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13412 * The function must be passed <ul>
13413 * <li>The Record block object</li>
13414 * <li>The "arg" argument from the load function</li>
13415 * <li>A boolean success indicator</li>
13417 * @param {Object} scope The scope in which to call the callback
13418 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13420 load : function(params, reader, callback, scope, arg){
13421 if(this.fireEvent("beforeload", this, params) !== false){
13423 params : params || {},
13425 callback : callback,
13430 callback : this.loadResponse,
13434 Roo.applyIf(o, this.conn);
13435 if(this.activeRequest){
13436 Roo.Ajax.abort(this.activeRequest);
13438 this.activeRequest = Roo.Ajax.request(o);
13440 this.conn.request(o);
13443 callback.call(scope||this, null, arg, false);
13448 loadResponse : function(o, success, response){
13449 delete this.activeRequest;
13451 this.fireEvent("loadexception", this, o, response);
13452 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13457 result = o.reader.read(response);
13459 this.fireEvent("loadexception", this, o, response, e);
13460 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13464 this.fireEvent("load", this, o, o.request.arg);
13465 o.request.callback.call(o.request.scope, result, o.request.arg, true);
13469 update : function(dataSet){
13474 updateResponse : function(dataSet){
13479 * Ext JS Library 1.1.1
13480 * Copyright(c) 2006-2007, Ext JS, LLC.
13482 * Originally Released Under LGPL - original licence link has changed is not relivant.
13485 * <script type="text/javascript">
13489 * @class Roo.data.ScriptTagProxy
13490 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13491 * other than the originating domain of the running page.<br><br>
13493 * <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
13494 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13496 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13497 * source code that is used as the source inside a <script> tag.<br><br>
13499 * In order for the browser to process the returned data, the server must wrap the data object
13500 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13501 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13502 * depending on whether the callback name was passed:
13505 boolean scriptTag = false;
13506 String cb = request.getParameter("callback");
13509 response.setContentType("text/javascript");
13511 response.setContentType("application/x-json");
13513 Writer out = response.getWriter();
13515 out.write(cb + "(");
13517 out.print(dataBlock.toJsonString());
13524 * @param {Object} config A configuration object.
13526 Roo.data.ScriptTagProxy = function(config){
13527 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13528 Roo.apply(this, config);
13529 this.head = document.getElementsByTagName("head")[0];
13532 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13534 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13536 * @cfg {String} url The URL from which to request the data object.
13539 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13543 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13544 * the server the name of the callback function set up by the load call to process the returned data object.
13545 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13546 * javascript output which calls this named function passing the data object as its only parameter.
13548 callbackParam : "callback",
13550 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13551 * name to the request.
13556 * Load data from the configured URL, read the data object into
13557 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13558 * process that block using the passed callback.
13559 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13560 * for the request to the remote server.
13561 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13562 * object into a block of Roo.data.Records.
13563 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13564 * The function must be passed <ul>
13565 * <li>The Record block object</li>
13566 * <li>The "arg" argument from the load function</li>
13567 * <li>A boolean success indicator</li>
13569 * @param {Object} scope The scope in which to call the callback
13570 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13572 load : function(params, reader, callback, scope, arg){
13573 if(this.fireEvent("beforeload", this, params) !== false){
13575 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13577 var url = this.url;
13578 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13580 url += "&_dc=" + (new Date().getTime());
13582 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13585 cb : "stcCallback"+transId,
13586 scriptId : "stcScript"+transId,
13590 callback : callback,
13596 window[trans.cb] = function(o){
13597 conn.handleResponse(o, trans);
13600 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13602 if(this.autoAbort !== false){
13606 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13608 var script = document.createElement("script");
13609 script.setAttribute("src", url);
13610 script.setAttribute("type", "text/javascript");
13611 script.setAttribute("id", trans.scriptId);
13612 this.head.appendChild(script);
13614 this.trans = trans;
13616 callback.call(scope||this, null, arg, false);
13621 isLoading : function(){
13622 return this.trans ? true : false;
13626 * Abort the current server request.
13628 abort : function(){
13629 if(this.isLoading()){
13630 this.destroyTrans(this.trans);
13635 destroyTrans : function(trans, isLoaded){
13636 this.head.removeChild(document.getElementById(trans.scriptId));
13637 clearTimeout(trans.timeoutId);
13639 window[trans.cb] = undefined;
13641 delete window[trans.cb];
13644 // if hasn't been loaded, wait for load to remove it to prevent script error
13645 window[trans.cb] = function(){
13646 window[trans.cb] = undefined;
13648 delete window[trans.cb];
13655 handleResponse : function(o, trans){
13656 this.trans = false;
13657 this.destroyTrans(trans, true);
13660 result = trans.reader.readRecords(o);
13662 this.fireEvent("loadexception", this, o, trans.arg, e);
13663 trans.callback.call(trans.scope||window, null, trans.arg, false);
13666 this.fireEvent("load", this, o, trans.arg);
13667 trans.callback.call(trans.scope||window, result, trans.arg, true);
13671 handleFailure : function(trans){
13672 this.trans = false;
13673 this.destroyTrans(trans, false);
13674 this.fireEvent("loadexception", this, null, trans.arg);
13675 trans.callback.call(trans.scope||window, null, trans.arg, false);
13679 * Ext JS Library 1.1.1
13680 * Copyright(c) 2006-2007, Ext JS, LLC.
13682 * Originally Released Under LGPL - original licence link has changed is not relivant.
13685 * <script type="text/javascript">
13689 * @class Roo.data.JsonReader
13690 * @extends Roo.data.DataReader
13691 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13692 * based on mappings in a provided Roo.data.Record constructor.
13694 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13695 * in the reply previously.
13700 var RecordDef = Roo.data.Record.create([
13701 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13702 {name: 'occupation'} // This field will use "occupation" as the mapping.
13704 var myReader = new Roo.data.JsonReader({
13705 totalProperty: "results", // The property which contains the total dataset size (optional)
13706 root: "rows", // The property which contains an Array of row objects
13707 id: "id" // The property within each row object that provides an ID for the record (optional)
13711 * This would consume a JSON file like this:
13713 { 'results': 2, 'rows': [
13714 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13715 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13718 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13719 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13720 * paged from the remote server.
13721 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13722 * @cfg {String} root name of the property which contains the Array of row objects.
13723 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13724 * @cfg {Array} fields Array of field definition objects
13726 * Create a new JsonReader
13727 * @param {Object} meta Metadata configuration options
13728 * @param {Object} recordType Either an Array of field definition objects,
13729 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13731 Roo.data.JsonReader = function(meta, recordType){
13734 // set some defaults:
13735 Roo.applyIf(meta, {
13736 totalProperty: 'total',
13737 successProperty : 'success',
13742 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13744 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13746 readerType : 'Json',
13749 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13750 * Used by Store query builder to append _requestMeta to params.
13753 metaFromRemote : false,
13755 * This method is only used by a DataProxy which has retrieved data from a remote server.
13756 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13757 * @return {Object} data A data block which is used by an Roo.data.Store object as
13758 * a cache of Roo.data.Records.
13760 read : function(response){
13761 var json = response.responseText;
13763 var o = /* eval:var:o */ eval("("+json+")");
13765 throw {message: "JsonReader.read: Json object not found"};
13771 this.metaFromRemote = true;
13772 this.meta = o.metaData;
13773 this.recordType = Roo.data.Record.create(o.metaData.fields);
13774 this.onMetaChange(this.meta, this.recordType, o);
13776 return this.readRecords(o);
13779 // private function a store will implement
13780 onMetaChange : function(meta, recordType, o){
13787 simpleAccess: function(obj, subsc) {
13794 getJsonAccessor: function(){
13796 return function(expr) {
13798 return(re.test(expr))
13799 ? new Function("obj", "return obj." + expr)
13804 return Roo.emptyFn;
13809 * Create a data block containing Roo.data.Records from an XML document.
13810 * @param {Object} o An object which contains an Array of row objects in the property specified
13811 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13812 * which contains the total size of the dataset.
13813 * @return {Object} data A data block which is used by an Roo.data.Store object as
13814 * a cache of Roo.data.Records.
13816 readRecords : function(o){
13818 * After any data loads, the raw JSON data is available for further custom processing.
13822 var s = this.meta, Record = this.recordType,
13823 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13825 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13827 if(s.totalProperty) {
13828 this.getTotal = this.getJsonAccessor(s.totalProperty);
13830 if(s.successProperty) {
13831 this.getSuccess = this.getJsonAccessor(s.successProperty);
13833 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13835 var g = this.getJsonAccessor(s.id);
13836 this.getId = function(rec) {
13838 return (r === undefined || r === "") ? null : r;
13841 this.getId = function(){return null;};
13844 for(var jj = 0; jj < fl; jj++){
13846 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13847 this.ef[jj] = this.getJsonAccessor(map);
13851 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13852 if(s.totalProperty){
13853 var vt = parseInt(this.getTotal(o), 10);
13858 if(s.successProperty){
13859 var vs = this.getSuccess(o);
13860 if(vs === false || vs === 'false'){
13865 for(var i = 0; i < c; i++){
13868 var id = this.getId(n);
13869 for(var j = 0; j < fl; j++){
13871 var v = this.ef[j](n);
13873 Roo.log('missing convert for ' + f.name);
13877 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13879 var record = new Record(values, id);
13881 records[i] = record;
13887 totalRecords : totalRecords
13890 // used when loading children.. @see loadDataFromChildren
13891 toLoadData: function(rec)
13893 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13894 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13895 return { data : data, total : data.length };
13900 * Ext JS Library 1.1.1
13901 * Copyright(c) 2006-2007, Ext JS, LLC.
13903 * Originally Released Under LGPL - original licence link has changed is not relivant.
13906 * <script type="text/javascript">
13910 * @class Roo.data.ArrayReader
13911 * @extends Roo.data.DataReader
13912 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13913 * Each element of that Array represents a row of data fields. The
13914 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13915 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13919 var RecordDef = Roo.data.Record.create([
13920 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13921 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13923 var myReader = new Roo.data.ArrayReader({
13924 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13928 * This would consume an Array like this:
13930 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13934 * Create a new JsonReader
13935 * @param {Object} meta Metadata configuration options.
13936 * @param {Object|Array} recordType Either an Array of field definition objects
13938 * @cfg {Array} fields Array of field definition objects
13939 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13940 * as specified to {@link Roo.data.Record#create},
13941 * or an {@link Roo.data.Record} object
13944 * created using {@link Roo.data.Record#create}.
13946 Roo.data.ArrayReader = function(meta, recordType)
13948 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13951 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13954 * Create a data block containing Roo.data.Records from an XML document.
13955 * @param {Object} o An Array of row objects which represents the dataset.
13956 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13957 * a cache of Roo.data.Records.
13959 readRecords : function(o)
13961 var sid = this.meta ? this.meta.id : null;
13962 var recordType = this.recordType, fields = recordType.prototype.fields;
13965 for(var i = 0; i < root.length; i++){
13968 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13969 for(var j = 0, jlen = fields.length; j < jlen; j++){
13970 var f = fields.items[j];
13971 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13972 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13974 values[f.name] = v;
13976 var record = new recordType(values, id);
13978 records[records.length] = record;
13982 totalRecords : records.length
13985 // used when loading children.. @see loadDataFromChildren
13986 toLoadData: function(rec)
13988 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13989 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14000 * @class Roo.bootstrap.ComboBox
14001 * @extends Roo.bootstrap.TriggerField
14002 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14003 * @cfg {Boolean} append (true|false) default false
14004 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14005 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14006 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14007 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14008 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14009 * @cfg {Boolean} animate default true
14010 * @cfg {Boolean} emptyResultText only for touch device
14011 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14012 * @cfg {String} emptyTitle default ''
14014 * Create a new ComboBox.
14015 * @param {Object} config Configuration options
14017 Roo.bootstrap.ComboBox = function(config){
14018 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14022 * Fires when the dropdown list is expanded
14023 * @param {Roo.bootstrap.ComboBox} combo This combo box
14028 * Fires when the dropdown list is collapsed
14029 * @param {Roo.bootstrap.ComboBox} combo This combo box
14033 * @event beforeselect
14034 * Fires before a list item is selected. Return false to cancel the selection.
14035 * @param {Roo.bootstrap.ComboBox} combo This combo box
14036 * @param {Roo.data.Record} record The data record returned from the underlying store
14037 * @param {Number} index The index of the selected item in the dropdown list
14039 'beforeselect' : true,
14042 * Fires when a list item is selected
14043 * @param {Roo.bootstrap.ComboBox} combo This combo box
14044 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14045 * @param {Number} index The index of the selected item in the dropdown list
14049 * @event beforequery
14050 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14051 * The event object passed has these properties:
14052 * @param {Roo.bootstrap.ComboBox} combo This combo box
14053 * @param {String} query The query
14054 * @param {Boolean} forceAll true to force "all" query
14055 * @param {Boolean} cancel true to cancel the query
14056 * @param {Object} e The query event object
14058 'beforequery': true,
14061 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14062 * @param {Roo.bootstrap.ComboBox} combo This combo box
14067 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14068 * @param {Roo.bootstrap.ComboBox} combo This combo box
14069 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14074 * Fires when the remove value from the combobox array
14075 * @param {Roo.bootstrap.ComboBox} combo This combo box
14079 * @event afterremove
14080 * Fires when the remove value from the combobox array
14081 * @param {Roo.bootstrap.ComboBox} combo This combo box
14083 'afterremove' : true,
14085 * @event specialfilter
14086 * Fires when specialfilter
14087 * @param {Roo.bootstrap.ComboBox} combo This combo box
14089 'specialfilter' : true,
14092 * Fires when tick the element
14093 * @param {Roo.bootstrap.ComboBox} combo This combo box
14097 * @event touchviewdisplay
14098 * Fires when touch view require special display (default is using displayField)
14099 * @param {Roo.bootstrap.ComboBox} combo This combo box
14100 * @param {Object} cfg set html .
14102 'touchviewdisplay' : true
14107 this.tickItems = [];
14109 this.selectedIndex = -1;
14110 if(this.mode == 'local'){
14111 if(config.queryDelay === undefined){
14112 this.queryDelay = 10;
14114 if(config.minChars === undefined){
14120 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14123 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14124 * rendering into an Roo.Editor, defaults to false)
14127 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14128 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14131 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14134 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14135 * the dropdown list (defaults to undefined, with no header element)
14139 * @cfg {String/Roo.Template} tpl The template to use to render the output
14143 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14145 listWidth: undefined,
14147 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14148 * mode = 'remote' or 'text' if mode = 'local')
14150 displayField: undefined,
14153 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14154 * mode = 'remote' or 'value' if mode = 'local').
14155 * Note: use of a valueField requires the user make a selection
14156 * in order for a value to be mapped.
14158 valueField: undefined,
14160 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14165 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14166 * field's data value (defaults to the underlying DOM element's name)
14168 hiddenName: undefined,
14170 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14174 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14176 selectedClass: 'active',
14179 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14183 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14184 * anchor positions (defaults to 'tl-bl')
14186 listAlign: 'tl-bl?',
14188 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14192 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14193 * query specified by the allQuery config option (defaults to 'query')
14195 triggerAction: 'query',
14197 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14198 * (defaults to 4, does not apply if editable = false)
14202 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14203 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14207 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14208 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14212 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14213 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14217 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14218 * when editable = true (defaults to false)
14220 selectOnFocus:false,
14222 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14224 queryParam: 'query',
14226 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14227 * when mode = 'remote' (defaults to 'Loading...')
14229 loadingText: 'Loading...',
14231 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14235 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14239 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14240 * traditional select (defaults to true)
14244 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14248 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14252 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14253 * listWidth has a higher value)
14257 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14258 * allow the user to set arbitrary text into the field (defaults to false)
14260 forceSelection:false,
14262 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14263 * if typeAhead = true (defaults to 250)
14265 typeAheadDelay : 250,
14267 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14268 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14270 valueNotFoundText : undefined,
14272 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14274 blockFocus : false,
14277 * @cfg {Boolean} disableClear Disable showing of clear button.
14279 disableClear : false,
14281 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14283 alwaysQuery : false,
14286 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14291 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14293 invalidClass : "has-warning",
14296 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14298 validClass : "has-success",
14301 * @cfg {Boolean} specialFilter (true|false) special filter default false
14303 specialFilter : false,
14306 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14308 mobileTouchView : true,
14311 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14313 useNativeIOS : false,
14316 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14318 mobile_restrict_height : false,
14320 ios_options : false,
14332 btnPosition : 'right',
14333 triggerList : true,
14334 showToggleBtn : true,
14336 emptyResultText: 'Empty',
14337 triggerText : 'Select',
14340 // element that contains real text value.. (when hidden is used..)
14342 getAutoCreate : function()
14347 * Render classic select for iso
14350 if(Roo.isIOS && this.useNativeIOS){
14351 cfg = this.getAutoCreateNativeIOS();
14359 if(Roo.isTouch && this.mobileTouchView){
14360 cfg = this.getAutoCreateTouchView();
14367 if(!this.tickable){
14368 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14373 * ComboBox with tickable selections
14376 var align = this.labelAlign || this.parentLabelAlign();
14379 cls : 'form-group roo-combobox-tickable' //input-group
14382 var btn_text_select = '';
14383 var btn_text_done = '';
14384 var btn_text_cancel = '';
14386 if (this.btn_text_show) {
14387 btn_text_select = 'Select';
14388 btn_text_done = 'Done';
14389 btn_text_cancel = 'Cancel';
14394 cls : 'tickable-buttons',
14399 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14400 //html : this.triggerText
14401 html: btn_text_select
14407 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14409 html: btn_text_done
14415 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14417 html: btn_text_cancel
14423 buttons.cn.unshift({
14425 cls: 'roo-select2-search-field-input'
14431 Roo.each(buttons.cn, function(c){
14433 c.cls += ' btn-' + _this.size;
14436 if (_this.disabled) {
14443 style : 'display: contents',
14448 cls: 'form-hidden-field'
14452 cls: 'roo-select2-choices',
14456 cls: 'roo-select2-search-field',
14467 cls: 'roo-select2-container input-group roo-select2-container-multi',
14473 // cls: 'typeahead typeahead-long dropdown-menu',
14474 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
14479 if(this.hasFeedback && !this.allowBlank){
14483 cls: 'glyphicon form-control-feedback'
14486 combobox.cn.push(feedback);
14491 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14492 tooltip : 'This field is required'
14494 if (Roo.bootstrap.version == 4) {
14497 style : 'display:none'
14500 if (align ==='left' && this.fieldLabel.length) {
14502 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14509 cls : 'control-label col-form-label',
14510 html : this.fieldLabel
14522 var labelCfg = cfg.cn[1];
14523 var contentCfg = cfg.cn[2];
14526 if(this.indicatorpos == 'right'){
14532 cls : 'control-label col-form-label',
14536 html : this.fieldLabel
14552 labelCfg = cfg.cn[0];
14553 contentCfg = cfg.cn[1];
14557 if(this.labelWidth > 12){
14558 labelCfg.style = "width: " + this.labelWidth + 'px';
14561 if(this.labelWidth < 13 && this.labelmd == 0){
14562 this.labelmd = this.labelWidth;
14565 if(this.labellg > 0){
14566 labelCfg.cls += ' col-lg-' + this.labellg;
14567 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14570 if(this.labelmd > 0){
14571 labelCfg.cls += ' col-md-' + this.labelmd;
14572 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14575 if(this.labelsm > 0){
14576 labelCfg.cls += ' col-sm-' + this.labelsm;
14577 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14580 if(this.labelxs > 0){
14581 labelCfg.cls += ' col-xs-' + this.labelxs;
14582 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14586 } else if ( this.fieldLabel.length) {
14587 // Roo.log(" label");
14592 //cls : 'input-group-addon',
14593 html : this.fieldLabel
14598 if(this.indicatorpos == 'right'){
14602 //cls : 'input-group-addon',
14603 html : this.fieldLabel
14613 // Roo.log(" no label && no align");
14620 ['xs','sm','md','lg'].map(function(size){
14621 if (settings[size]) {
14622 cfg.cls += ' col-' + size + '-' + settings[size];
14630 _initEventsCalled : false,
14633 initEvents: function()
14635 if (this._initEventsCalled) { // as we call render... prevent looping...
14638 this._initEventsCalled = true;
14641 throw "can not find store for combo";
14644 this.indicator = this.indicatorEl();
14646 this.store = Roo.factory(this.store, Roo.data);
14647 this.store.parent = this;
14649 // if we are building from html. then this element is so complex, that we can not really
14650 // use the rendered HTML.
14651 // so we have to trash and replace the previous code.
14652 if (Roo.XComponent.build_from_html) {
14653 // remove this element....
14654 var e = this.el.dom, k=0;
14655 while (e ) { e = e.previousSibling; ++k;}
14660 this.rendered = false;
14662 this.render(this.parent().getChildContainer(true), k);
14665 if(Roo.isIOS && this.useNativeIOS){
14666 this.initIOSView();
14674 if(Roo.isTouch && this.mobileTouchView){
14675 this.initTouchView();
14680 this.initTickableEvents();
14684 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14686 if(this.hiddenName){
14688 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14690 this.hiddenField.dom.value =
14691 this.hiddenValue !== undefined ? this.hiddenValue :
14692 this.value !== undefined ? this.value : '';
14694 // prevent input submission
14695 this.el.dom.removeAttribute('name');
14696 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14701 // this.el.dom.setAttribute('autocomplete', 'off');
14704 var cls = 'x-combo-list';
14706 //this.list = new Roo.Layer({
14707 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14713 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14714 _this.list.setWidth(lw);
14717 this.list.on('mouseover', this.onViewOver, this);
14718 this.list.on('mousemove', this.onViewMove, this);
14719 this.list.on('scroll', this.onViewScroll, this);
14722 this.list.swallowEvent('mousewheel');
14723 this.assetHeight = 0;
14726 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14727 this.assetHeight += this.header.getHeight();
14730 this.innerList = this.list.createChild({cls:cls+'-inner'});
14731 this.innerList.on('mouseover', this.onViewOver, this);
14732 this.innerList.on('mousemove', this.onViewMove, this);
14733 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14735 if(this.allowBlank && !this.pageSize && !this.disableClear){
14736 this.footer = this.list.createChild({cls:cls+'-ft'});
14737 this.pageTb = new Roo.Toolbar(this.footer);
14741 this.footer = this.list.createChild({cls:cls+'-ft'});
14742 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14743 {pageSize: this.pageSize});
14747 if (this.pageTb && this.allowBlank && !this.disableClear) {
14749 this.pageTb.add(new Roo.Toolbar.Fill(), {
14750 cls: 'x-btn-icon x-btn-clear',
14752 handler: function()
14755 _this.clearValue();
14756 _this.onSelect(false, -1);
14761 this.assetHeight += this.footer.getHeight();
14766 this.tpl = Roo.bootstrap.version == 4 ?
14767 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14768 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14771 this.view = new Roo.View(this.list, this.tpl, {
14772 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14774 //this.view.wrapEl.setDisplayed(false);
14775 this.view.on('click', this.onViewClick, this);
14778 this.store.on('beforeload', this.onBeforeLoad, this);
14779 this.store.on('load', this.onLoad, this);
14780 this.store.on('loadexception', this.onLoadException, this);
14782 if(this.resizable){
14783 this.resizer = new Roo.Resizable(this.list, {
14784 pinned:true, handles:'se'
14786 this.resizer.on('resize', function(r, w, h){
14787 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14788 this.listWidth = w;
14789 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14790 this.restrictHeight();
14792 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14795 if(!this.editable){
14796 this.editable = true;
14797 this.setEditable(false);
14802 if (typeof(this.events.add.listeners) != 'undefined') {
14804 this.addicon = this.wrap.createChild(
14805 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14807 this.addicon.on('click', function(e) {
14808 this.fireEvent('add', this);
14811 if (typeof(this.events.edit.listeners) != 'undefined') {
14813 this.editicon = this.wrap.createChild(
14814 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14815 if (this.addicon) {
14816 this.editicon.setStyle('margin-left', '40px');
14818 this.editicon.on('click', function(e) {
14820 // we fire even if inothing is selected..
14821 this.fireEvent('edit', this, this.lastData );
14827 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14828 "up" : function(e){
14829 this.inKeyMode = true;
14833 "down" : function(e){
14834 if(!this.isExpanded()){
14835 this.onTriggerClick();
14837 this.inKeyMode = true;
14842 "enter" : function(e){
14843 // this.onViewClick();
14847 if(this.fireEvent("specialkey", this, e)){
14848 this.onViewClick(false);
14854 "esc" : function(e){
14858 "tab" : function(e){
14861 if(this.fireEvent("specialkey", this, e)){
14862 this.onViewClick(false);
14870 doRelay : function(foo, bar, hname){
14871 if(hname == 'down' || this.scope.isExpanded()){
14872 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14881 this.queryDelay = Math.max(this.queryDelay || 10,
14882 this.mode == 'local' ? 10 : 250);
14885 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14887 if(this.typeAhead){
14888 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14890 if(this.editable !== false){
14891 this.inputEl().on("keyup", this.onKeyUp, this);
14893 if(this.forceSelection){
14894 this.inputEl().on('blur', this.doForce, this);
14898 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14899 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14903 initTickableEvents: function()
14907 if(this.hiddenName){
14909 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14911 this.hiddenField.dom.value =
14912 this.hiddenValue !== undefined ? this.hiddenValue :
14913 this.value !== undefined ? this.value : '';
14915 // prevent input submission
14916 this.el.dom.removeAttribute('name');
14917 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14922 // this.list = this.el.select('ul.dropdown-menu',true).first();
14924 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14925 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14926 if(this.triggerList){
14927 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14930 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14931 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14933 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14934 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14936 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14937 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14939 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14940 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14941 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14944 this.cancelBtn.hide();
14949 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14950 _this.list.setWidth(lw);
14953 this.list.on('mouseover', this.onViewOver, this);
14954 this.list.on('mousemove', this.onViewMove, this);
14956 this.list.on('scroll', this.onViewScroll, this);
14959 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14960 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14963 this.view = new Roo.View(this.list, this.tpl, {
14968 selectedClass: this.selectedClass
14971 //this.view.wrapEl.setDisplayed(false);
14972 this.view.on('click', this.onViewClick, this);
14976 this.store.on('beforeload', this.onBeforeLoad, this);
14977 this.store.on('load', this.onLoad, this);
14978 this.store.on('loadexception', this.onLoadException, this);
14981 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14982 "up" : function(e){
14983 this.inKeyMode = true;
14987 "down" : function(e){
14988 this.inKeyMode = true;
14992 "enter" : function(e){
14993 if(this.fireEvent("specialkey", this, e)){
14994 this.onViewClick(false);
15000 "esc" : function(e){
15001 this.onTickableFooterButtonClick(e, false, false);
15004 "tab" : function(e){
15005 this.fireEvent("specialkey", this, e);
15007 this.onTickableFooterButtonClick(e, false, false);
15014 doRelay : function(e, fn, key){
15015 if(this.scope.isExpanded()){
15016 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15025 this.queryDelay = Math.max(this.queryDelay || 10,
15026 this.mode == 'local' ? 10 : 250);
15029 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15031 if(this.typeAhead){
15032 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15035 if(this.editable !== false){
15036 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15039 this.indicator = this.indicatorEl();
15041 if(this.indicator){
15042 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15043 this.indicator.hide();
15048 onDestroy : function(){
15050 this.view.setStore(null);
15051 this.view.el.removeAllListeners();
15052 this.view.el.remove();
15053 this.view.purgeListeners();
15056 this.list.dom.innerHTML = '';
15060 this.store.un('beforeload', this.onBeforeLoad, this);
15061 this.store.un('load', this.onLoad, this);
15062 this.store.un('loadexception', this.onLoadException, this);
15064 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15068 fireKey : function(e){
15069 if(e.isNavKeyPress() && !this.list.isVisible()){
15070 this.fireEvent("specialkey", this, e);
15075 onResize: function(w, h){
15076 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15078 // if(typeof w != 'number'){
15079 // // we do not handle it!?!?
15082 // var tw = this.trigger.getWidth();
15083 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15084 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15086 // this.inputEl().setWidth( this.adjustWidth('input', x));
15088 // //this.trigger.setStyle('left', x+'px');
15090 // if(this.list && this.listWidth === undefined){
15091 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15092 // this.list.setWidth(lw);
15093 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15101 * Allow or prevent the user from directly editing the field text. If false is passed,
15102 * the user will only be able to select from the items defined in the dropdown list. This method
15103 * is the runtime equivalent of setting the 'editable' config option at config time.
15104 * @param {Boolean} value True to allow the user to directly edit the field text
15106 setEditable : function(value){
15107 if(value == this.editable){
15110 this.editable = value;
15112 this.inputEl().dom.setAttribute('readOnly', true);
15113 this.inputEl().on('mousedown', this.onTriggerClick, this);
15114 this.inputEl().addClass('x-combo-noedit');
15116 this.inputEl().dom.setAttribute('readOnly', false);
15117 this.inputEl().un('mousedown', this.onTriggerClick, this);
15118 this.inputEl().removeClass('x-combo-noedit');
15124 onBeforeLoad : function(combo,opts){
15125 if(!this.hasFocus){
15129 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15131 this.restrictHeight();
15132 this.selectedIndex = -1;
15136 onLoad : function(){
15138 this.hasQuery = false;
15140 if(!this.hasFocus){
15144 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15145 this.loading.hide();
15148 if(this.store.getCount() > 0){
15151 this.restrictHeight();
15152 if(this.lastQuery == this.allQuery){
15153 if(this.editable && !this.tickable){
15154 this.inputEl().dom.select();
15158 !this.selectByValue(this.value, true) &&
15161 !this.store.lastOptions ||
15162 typeof(this.store.lastOptions.add) == 'undefined' ||
15163 this.store.lastOptions.add != true
15166 this.select(0, true);
15169 if(this.autoFocus){
15172 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15173 this.taTask.delay(this.typeAheadDelay);
15177 this.onEmptyResults();
15183 onLoadException : function()
15185 this.hasQuery = false;
15187 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15188 this.loading.hide();
15191 if(this.tickable && this.editable){
15196 // only causes errors at present
15197 //Roo.log(this.store.reader.jsonData);
15198 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15200 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15206 onTypeAhead : function(){
15207 if(this.store.getCount() > 0){
15208 var r = this.store.getAt(0);
15209 var newValue = r.data[this.displayField];
15210 var len = newValue.length;
15211 var selStart = this.getRawValue().length;
15213 if(selStart != len){
15214 this.setRawValue(newValue);
15215 this.selectText(selStart, newValue.length);
15221 onSelect : function(record, index){
15223 if(this.fireEvent('beforeselect', this, record, index) !== false){
15225 this.setFromData(index > -1 ? record.data : false);
15228 this.fireEvent('select', this, record, index);
15233 * Returns the currently selected field value or empty string if no value is set.
15234 * @return {String} value The selected value
15236 getValue : function()
15238 if(Roo.isIOS && this.useNativeIOS){
15239 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15243 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15246 if(this.valueField){
15247 return typeof this.value != 'undefined' ? this.value : '';
15249 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15253 getRawValue : function()
15255 if(Roo.isIOS && this.useNativeIOS){
15256 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15259 var v = this.inputEl().getValue();
15265 * Clears any text/value currently set in the field
15267 clearValue : function(){
15269 if(this.hiddenField){
15270 this.hiddenField.dom.value = '';
15273 this.setRawValue('');
15274 this.lastSelectionText = '';
15275 this.lastData = false;
15277 var close = this.closeTriggerEl();
15288 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15289 * will be displayed in the field. If the value does not match the data value of an existing item,
15290 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15291 * Otherwise the field will be blank (although the value will still be set).
15292 * @param {String} value The value to match
15294 setValue : function(v)
15296 if(Roo.isIOS && this.useNativeIOS){
15297 this.setIOSValue(v);
15307 if(this.valueField){
15308 var r = this.findRecord(this.valueField, v);
15310 text = r.data[this.displayField];
15311 }else if(this.valueNotFoundText !== undefined){
15312 text = this.valueNotFoundText;
15315 this.lastSelectionText = text;
15316 if(this.hiddenField){
15317 this.hiddenField.dom.value = v;
15319 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15322 var close = this.closeTriggerEl();
15325 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15331 * @property {Object} the last set data for the element
15336 * Sets the value of the field based on a object which is related to the record format for the store.
15337 * @param {Object} value the value to set as. or false on reset?
15339 setFromData : function(o){
15346 var dv = ''; // display value
15347 var vv = ''; // value value..
15349 if (this.displayField) {
15350 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15352 // this is an error condition!!!
15353 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15356 if(this.valueField){
15357 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15360 var close = this.closeTriggerEl();
15363 if(dv.length || vv * 1 > 0){
15365 this.blockFocus=true;
15371 if(this.hiddenField){
15372 this.hiddenField.dom.value = vv;
15374 this.lastSelectionText = dv;
15375 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15379 // no hidden field.. - we store the value in 'value', but still display
15380 // display field!!!!
15381 this.lastSelectionText = dv;
15382 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15389 reset : function(){
15390 // overridden so that last data is reset..
15397 this.setValue(this.originalValue);
15398 //this.clearInvalid();
15399 this.lastData = false;
15401 this.view.clearSelections();
15407 findRecord : function(prop, value){
15409 if(this.store.getCount() > 0){
15410 this.store.each(function(r){
15411 if(r.data[prop] == value){
15421 getName: function()
15423 // returns hidden if it's set..
15424 if (!this.rendered) {return ''};
15425 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
15429 onViewMove : function(e, t){
15430 this.inKeyMode = false;
15434 onViewOver : function(e, t){
15435 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15438 var item = this.view.findItemFromChild(t);
15441 var index = this.view.indexOf(item);
15442 this.select(index, false);
15447 onViewClick : function(view, doFocus, el, e)
15449 var index = this.view.getSelectedIndexes()[0];
15451 var r = this.store.getAt(index);
15455 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15462 Roo.each(this.tickItems, function(v,k){
15464 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15466 _this.tickItems.splice(k, 1);
15468 if(typeof(e) == 'undefined' && view == false){
15469 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15481 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15482 this.tickItems.push(r.data);
15485 if(typeof(e) == 'undefined' && view == false){
15486 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15493 this.onSelect(r, index);
15495 if(doFocus !== false && !this.blockFocus){
15496 this.inputEl().focus();
15501 restrictHeight : function(){
15502 //this.innerList.dom.style.height = '';
15503 //var inner = this.innerList.dom;
15504 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15505 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15506 //this.list.beginUpdate();
15507 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15508 this.list.alignTo(this.inputEl(), this.listAlign);
15509 this.list.alignTo(this.inputEl(), this.listAlign);
15510 //this.list.endUpdate();
15514 onEmptyResults : function(){
15516 if(this.tickable && this.editable){
15517 this.hasFocus = false;
15518 this.restrictHeight();
15526 * Returns true if the dropdown list is expanded, else false.
15528 isExpanded : function(){
15529 return this.list.isVisible();
15533 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15534 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15535 * @param {String} value The data value of the item to select
15536 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15537 * selected item if it is not currently in view (defaults to true)
15538 * @return {Boolean} True if the value matched an item in the list, else false
15540 selectByValue : function(v, scrollIntoView){
15541 if(v !== undefined && v !== null){
15542 var r = this.findRecord(this.valueField || this.displayField, v);
15544 this.select(this.store.indexOf(r), scrollIntoView);
15552 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15553 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15554 * @param {Number} index The zero-based index of the list item to select
15555 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15556 * selected item if it is not currently in view (defaults to true)
15558 select : function(index, scrollIntoView){
15559 this.selectedIndex = index;
15560 this.view.select(index);
15561 if(scrollIntoView !== false){
15562 var el = this.view.getNode(index);
15564 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15567 this.list.scrollChildIntoView(el, false);
15573 selectNext : function(){
15574 var ct = this.store.getCount();
15576 if(this.selectedIndex == -1){
15578 }else if(this.selectedIndex < ct-1){
15579 this.select(this.selectedIndex+1);
15585 selectPrev : function(){
15586 var ct = this.store.getCount();
15588 if(this.selectedIndex == -1){
15590 }else if(this.selectedIndex != 0){
15591 this.select(this.selectedIndex-1);
15597 onKeyUp : function(e){
15598 if(this.editable !== false && !e.isSpecialKey()){
15599 this.lastKey = e.getKey();
15600 this.dqTask.delay(this.queryDelay);
15605 validateBlur : function(){
15606 return !this.list || !this.list.isVisible();
15610 initQuery : function(){
15612 var v = this.getRawValue();
15614 if(this.tickable && this.editable){
15615 v = this.tickableInputEl().getValue();
15622 doForce : function(){
15623 if(this.inputEl().dom.value.length > 0){
15624 this.inputEl().dom.value =
15625 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15631 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15632 * query allowing the query action to be canceled if needed.
15633 * @param {String} query The SQL query to execute
15634 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15635 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15636 * saved in the current store (defaults to false)
15638 doQuery : function(q, forceAll){
15640 if(q === undefined || q === null){
15645 forceAll: forceAll,
15649 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15654 forceAll = qe.forceAll;
15655 if(forceAll === true || (q.length >= this.minChars)){
15657 this.hasQuery = true;
15659 if(this.lastQuery != q || this.alwaysQuery){
15660 this.lastQuery = q;
15661 if(this.mode == 'local'){
15662 this.selectedIndex = -1;
15664 this.store.clearFilter();
15667 if(this.specialFilter){
15668 this.fireEvent('specialfilter', this);
15673 this.store.filter(this.displayField, q);
15676 this.store.fireEvent("datachanged", this.store);
15683 this.store.baseParams[this.queryParam] = q;
15685 var options = {params : this.getParams(q)};
15688 options.add = true;
15689 options.params.start = this.page * this.pageSize;
15692 this.store.load(options);
15695 * this code will make the page width larger, at the beginning, the list not align correctly,
15696 * we should expand the list on onLoad
15697 * so command out it
15702 this.selectedIndex = -1;
15707 this.loadNext = false;
15711 getParams : function(q){
15713 //p[this.queryParam] = q;
15717 p.limit = this.pageSize;
15723 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15725 collapse : function(){
15726 if(!this.isExpanded()){
15732 this.hasFocus = false;
15736 this.cancelBtn.hide();
15737 this.trigger.show();
15740 this.tickableInputEl().dom.value = '';
15741 this.tickableInputEl().blur();
15746 Roo.get(document).un('mousedown', this.collapseIf, this);
15747 Roo.get(document).un('mousewheel', this.collapseIf, this);
15748 if (!this.editable) {
15749 Roo.get(document).un('keydown', this.listKeyPress, this);
15751 this.fireEvent('collapse', this);
15757 collapseIf : function(e){
15758 var in_combo = e.within(this.el);
15759 var in_list = e.within(this.list);
15760 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15762 if (in_combo || in_list || is_list) {
15763 //e.stopPropagation();
15768 this.onTickableFooterButtonClick(e, false, false);
15776 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15778 expand : function(){
15780 if(this.isExpanded() || !this.hasFocus){
15784 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15785 this.list.setWidth(lw);
15791 this.restrictHeight();
15795 this.tickItems = Roo.apply([], this.item);
15798 this.cancelBtn.show();
15799 this.trigger.hide();
15802 this.tickableInputEl().focus();
15807 Roo.get(document).on('mousedown', this.collapseIf, this);
15808 Roo.get(document).on('mousewheel', this.collapseIf, this);
15809 if (!this.editable) {
15810 Roo.get(document).on('keydown', this.listKeyPress, this);
15813 this.fireEvent('expand', this);
15817 // Implements the default empty TriggerField.onTriggerClick function
15818 onTriggerClick : function(e)
15820 Roo.log('trigger click');
15822 if(this.disabled || !this.triggerList){
15827 this.loadNext = false;
15829 if(this.isExpanded()){
15831 if (!this.blockFocus) {
15832 this.inputEl().focus();
15836 this.hasFocus = true;
15837 if(this.triggerAction == 'all') {
15838 this.doQuery(this.allQuery, true);
15840 this.doQuery(this.getRawValue());
15842 if (!this.blockFocus) {
15843 this.inputEl().focus();
15848 onTickableTriggerClick : function(e)
15855 this.loadNext = false;
15856 this.hasFocus = true;
15858 if(this.triggerAction == 'all') {
15859 this.doQuery(this.allQuery, true);
15861 this.doQuery(this.getRawValue());
15865 onSearchFieldClick : function(e)
15867 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15868 this.onTickableFooterButtonClick(e, false, false);
15872 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15877 this.loadNext = false;
15878 this.hasFocus = true;
15880 if(this.triggerAction == 'all') {
15881 this.doQuery(this.allQuery, true);
15883 this.doQuery(this.getRawValue());
15887 listKeyPress : function(e)
15889 //Roo.log('listkeypress');
15890 // scroll to first matching element based on key pres..
15891 if (e.isSpecialKey()) {
15894 var k = String.fromCharCode(e.getKey()).toUpperCase();
15897 var csel = this.view.getSelectedNodes();
15898 var cselitem = false;
15900 var ix = this.view.indexOf(csel[0]);
15901 cselitem = this.store.getAt(ix);
15902 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15908 this.store.each(function(v) {
15910 // start at existing selection.
15911 if (cselitem.id == v.id) {
15917 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15918 match = this.store.indexOf(v);
15924 if (match === false) {
15925 return true; // no more action?
15928 this.view.select(match);
15929 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15930 sn.scrollIntoView(sn.dom.parentNode, false);
15933 onViewScroll : function(e, t){
15935 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){
15939 this.hasQuery = true;
15941 this.loading = this.list.select('.loading', true).first();
15943 if(this.loading === null){
15944 this.list.createChild({
15946 cls: 'loading roo-select2-more-results roo-select2-active',
15947 html: 'Loading more results...'
15950 this.loading = this.list.select('.loading', true).first();
15952 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15954 this.loading.hide();
15957 this.loading.show();
15962 this.loadNext = true;
15964 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15969 addItem : function(o)
15971 var dv = ''; // display value
15973 if (this.displayField) {
15974 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15976 // this is an error condition!!!
15977 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15984 var choice = this.choices.createChild({
15986 cls: 'roo-select2-search-choice',
15995 cls: 'roo-select2-search-choice-close fa fa-times',
16000 }, this.searchField);
16002 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16004 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16012 this.inputEl().dom.value = '';
16017 onRemoveItem : function(e, _self, o)
16019 e.preventDefault();
16021 this.lastItem = Roo.apply([], this.item);
16023 var index = this.item.indexOf(o.data) * 1;
16026 Roo.log('not this item?!');
16030 this.item.splice(index, 1);
16035 this.fireEvent('remove', this, e);
16041 syncValue : function()
16043 if(!this.item.length){
16050 Roo.each(this.item, function(i){
16051 if(_this.valueField){
16052 value.push(i[_this.valueField]);
16059 this.value = value.join(',');
16061 if(this.hiddenField){
16062 this.hiddenField.dom.value = this.value;
16065 this.store.fireEvent("datachanged", this.store);
16070 clearItem : function()
16072 if(!this.multiple){
16078 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16086 if(this.tickable && !Roo.isTouch){
16087 this.view.refresh();
16091 inputEl: function ()
16093 if(Roo.isIOS && this.useNativeIOS){
16094 return this.el.select('select.roo-ios-select', true).first();
16097 if(Roo.isTouch && this.mobileTouchView){
16098 return this.el.select('input.form-control',true).first();
16102 return this.searchField;
16105 return this.el.select('input.form-control',true).first();
16108 onTickableFooterButtonClick : function(e, btn, el)
16110 e.preventDefault();
16112 this.lastItem = Roo.apply([], this.item);
16114 if(btn && btn.name == 'cancel'){
16115 this.tickItems = Roo.apply([], this.item);
16124 Roo.each(this.tickItems, function(o){
16132 validate : function()
16134 if(this.getVisibilityEl().hasClass('hidden')){
16138 var v = this.getRawValue();
16141 v = this.getValue();
16144 if(this.disabled || this.allowBlank || v.length){
16149 this.markInvalid();
16153 tickableInputEl : function()
16155 if(!this.tickable || !this.editable){
16156 return this.inputEl();
16159 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16163 getAutoCreateTouchView : function()
16168 cls: 'form-group' //input-group
16174 type : this.inputType,
16175 cls : 'form-control x-combo-noedit',
16176 autocomplete: 'new-password',
16177 placeholder : this.placeholder || '',
16182 input.name = this.name;
16186 input.cls += ' input-' + this.size;
16189 if (this.disabled) {
16190 input.disabled = true;
16201 inputblock.cls += ' input-group';
16203 inputblock.cn.unshift({
16205 cls : 'input-group-addon input-group-prepend input-group-text',
16210 if(this.removable && !this.multiple){
16211 inputblock.cls += ' roo-removable';
16213 inputblock.cn.push({
16216 cls : 'roo-combo-removable-btn close'
16220 if(this.hasFeedback && !this.allowBlank){
16222 inputblock.cls += ' has-feedback';
16224 inputblock.cn.push({
16226 cls: 'glyphicon form-control-feedback'
16233 inputblock.cls += (this.before) ? '' : ' input-group';
16235 inputblock.cn.push({
16237 cls : 'input-group-addon input-group-append input-group-text',
16243 var ibwrap = inputblock;
16248 cls: 'roo-select2-choices',
16252 cls: 'roo-select2-search-field',
16265 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16270 cls: 'form-hidden-field'
16276 if(!this.multiple && this.showToggleBtn){
16282 if (this.caret != false) {
16285 cls: 'fa fa-' + this.caret
16292 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16294 Roo.bootstrap.version == 3 ? caret : '',
16297 cls: 'combobox-clear',
16311 combobox.cls += ' roo-select2-container-multi';
16314 var align = this.labelAlign || this.parentLabelAlign();
16316 if (align ==='left' && this.fieldLabel.length) {
16321 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16322 tooltip : 'This field is required'
16326 cls : 'control-label col-form-label',
16327 html : this.fieldLabel
16338 var labelCfg = cfg.cn[1];
16339 var contentCfg = cfg.cn[2];
16342 if(this.indicatorpos == 'right'){
16347 cls : 'control-label col-form-label',
16351 html : this.fieldLabel
16355 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16356 tooltip : 'This field is required'
16369 labelCfg = cfg.cn[0];
16370 contentCfg = cfg.cn[1];
16375 if(this.labelWidth > 12){
16376 labelCfg.style = "width: " + this.labelWidth + 'px';
16379 if(this.labelWidth < 13 && this.labelmd == 0){
16380 this.labelmd = this.labelWidth;
16383 if(this.labellg > 0){
16384 labelCfg.cls += ' col-lg-' + this.labellg;
16385 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16388 if(this.labelmd > 0){
16389 labelCfg.cls += ' col-md-' + this.labelmd;
16390 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16393 if(this.labelsm > 0){
16394 labelCfg.cls += ' col-sm-' + this.labelsm;
16395 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16398 if(this.labelxs > 0){
16399 labelCfg.cls += ' col-xs-' + this.labelxs;
16400 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16404 } else if ( this.fieldLabel.length) {
16408 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16409 tooltip : 'This field is required'
16413 cls : 'control-label',
16414 html : this.fieldLabel
16425 if(this.indicatorpos == 'right'){
16429 cls : 'control-label',
16430 html : this.fieldLabel,
16434 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16435 tooltip : 'This field is required'
16452 var settings = this;
16454 ['xs','sm','md','lg'].map(function(size){
16455 if (settings[size]) {
16456 cfg.cls += ' col-' + size + '-' + settings[size];
16463 initTouchView : function()
16465 this.renderTouchView();
16467 this.touchViewEl.on('scroll', function(){
16468 this.el.dom.scrollTop = 0;
16471 this.originalValue = this.getValue();
16473 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16475 this.inputEl().on("click", this.showTouchView, this);
16476 if (this.triggerEl) {
16477 this.triggerEl.on("click", this.showTouchView, this);
16481 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16482 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16484 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16486 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16487 this.store.on('load', this.onTouchViewLoad, this);
16488 this.store.on('loadexception', this.onTouchViewLoadException, this);
16490 if(this.hiddenName){
16492 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16494 this.hiddenField.dom.value =
16495 this.hiddenValue !== undefined ? this.hiddenValue :
16496 this.value !== undefined ? this.value : '';
16498 this.el.dom.removeAttribute('name');
16499 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16503 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16504 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16507 if(this.removable && !this.multiple){
16508 var close = this.closeTriggerEl();
16510 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16511 close.on('click', this.removeBtnClick, this, close);
16515 * fix the bug in Safari iOS8
16517 this.inputEl().on("focus", function(e){
16518 document.activeElement.blur();
16521 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16528 renderTouchView : function()
16530 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16531 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16533 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16534 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16536 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16537 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16538 this.touchViewBodyEl.setStyle('overflow', 'auto');
16540 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16541 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16543 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16544 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16548 showTouchView : function()
16554 this.touchViewHeaderEl.hide();
16556 if(this.modalTitle.length){
16557 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16558 this.touchViewHeaderEl.show();
16561 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16562 this.touchViewEl.show();
16564 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16566 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16567 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16569 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16571 if(this.modalTitle.length){
16572 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16575 this.touchViewBodyEl.setHeight(bodyHeight);
16579 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16581 this.touchViewEl.addClass('in');
16584 if(this._touchViewMask){
16585 Roo.get(document.body).addClass("x-body-masked");
16586 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16587 this._touchViewMask.setStyle('z-index', 10000);
16588 this._touchViewMask.addClass('show');
16591 this.doTouchViewQuery();
16595 hideTouchView : function()
16597 this.touchViewEl.removeClass('in');
16601 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16603 this.touchViewEl.setStyle('display', 'none');
16606 if(this._touchViewMask){
16607 this._touchViewMask.removeClass('show');
16608 Roo.get(document.body).removeClass("x-body-masked");
16612 setTouchViewValue : function()
16619 Roo.each(this.tickItems, function(o){
16624 this.hideTouchView();
16627 doTouchViewQuery : function()
16636 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16640 if(!this.alwaysQuery || this.mode == 'local'){
16641 this.onTouchViewLoad();
16648 onTouchViewBeforeLoad : function(combo,opts)
16654 onTouchViewLoad : function()
16656 if(this.store.getCount() < 1){
16657 this.onTouchViewEmptyResults();
16661 this.clearTouchView();
16663 var rawValue = this.getRawValue();
16665 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16667 this.tickItems = [];
16669 this.store.data.each(function(d, rowIndex){
16670 var row = this.touchViewListGroup.createChild(template);
16672 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16673 row.addClass(d.data.cls);
16676 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16679 html : d.data[this.displayField]
16682 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16683 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16686 row.removeClass('selected');
16687 if(!this.multiple && this.valueField &&
16688 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16691 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16692 row.addClass('selected');
16695 if(this.multiple && this.valueField &&
16696 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16700 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16701 this.tickItems.push(d.data);
16704 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16708 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16710 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16712 if(this.modalTitle.length){
16713 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16716 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16718 if(this.mobile_restrict_height && listHeight < bodyHeight){
16719 this.touchViewBodyEl.setHeight(listHeight);
16724 if(firstChecked && listHeight > bodyHeight){
16725 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16730 onTouchViewLoadException : function()
16732 this.hideTouchView();
16735 onTouchViewEmptyResults : function()
16737 this.clearTouchView();
16739 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16741 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16745 clearTouchView : function()
16747 this.touchViewListGroup.dom.innerHTML = '';
16750 onTouchViewClick : function(e, el, o)
16752 e.preventDefault();
16755 var rowIndex = o.rowIndex;
16757 var r = this.store.getAt(rowIndex);
16759 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16761 if(!this.multiple){
16762 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16763 c.dom.removeAttribute('checked');
16766 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16768 this.setFromData(r.data);
16770 var close = this.closeTriggerEl();
16776 this.hideTouchView();
16778 this.fireEvent('select', this, r, rowIndex);
16783 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16784 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16785 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16789 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16790 this.addItem(r.data);
16791 this.tickItems.push(r.data);
16795 getAutoCreateNativeIOS : function()
16798 cls: 'form-group' //input-group,
16803 cls : 'roo-ios-select'
16807 combobox.name = this.name;
16810 if (this.disabled) {
16811 combobox.disabled = true;
16814 var settings = this;
16816 ['xs','sm','md','lg'].map(function(size){
16817 if (settings[size]) {
16818 cfg.cls += ' col-' + size + '-' + settings[size];
16828 initIOSView : function()
16830 this.store.on('load', this.onIOSViewLoad, this);
16835 onIOSViewLoad : function()
16837 if(this.store.getCount() < 1){
16841 this.clearIOSView();
16843 if(this.allowBlank) {
16845 var default_text = '-- SELECT --';
16847 if(this.placeholder.length){
16848 default_text = this.placeholder;
16851 if(this.emptyTitle.length){
16852 default_text += ' - ' + this.emptyTitle + ' -';
16855 var opt = this.inputEl().createChild({
16858 html : default_text
16862 o[this.valueField] = 0;
16863 o[this.displayField] = default_text;
16865 this.ios_options.push({
16872 this.store.data.each(function(d, rowIndex){
16876 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16877 html = d.data[this.displayField];
16882 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16883 value = d.data[this.valueField];
16892 if(this.value == d.data[this.valueField]){
16893 option['selected'] = true;
16896 var opt = this.inputEl().createChild(option);
16898 this.ios_options.push({
16905 this.inputEl().on('change', function(){
16906 this.fireEvent('select', this);
16911 clearIOSView: function()
16913 this.inputEl().dom.innerHTML = '';
16915 this.ios_options = [];
16918 setIOSValue: function(v)
16922 if(!this.ios_options){
16926 Roo.each(this.ios_options, function(opts){
16928 opts.el.dom.removeAttribute('selected');
16930 if(opts.data[this.valueField] != v){
16934 opts.el.dom.setAttribute('selected', true);
16940 * @cfg {Boolean} grow
16944 * @cfg {Number} growMin
16948 * @cfg {Number} growMax
16957 Roo.apply(Roo.bootstrap.ComboBox, {
16961 cls: 'modal-header',
16983 cls: 'list-group-item',
16987 cls: 'roo-combobox-list-group-item-value'
16991 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17005 listItemCheckbox : {
17007 cls: 'list-group-item',
17011 cls: 'roo-combobox-list-group-item-value'
17015 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17031 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17036 cls: 'modal-footer',
17044 cls: 'col-xs-6 text-left',
17047 cls: 'btn btn-danger roo-touch-view-cancel',
17053 cls: 'col-xs-6 text-right',
17056 cls: 'btn btn-success roo-touch-view-ok',
17067 Roo.apply(Roo.bootstrap.ComboBox, {
17069 touchViewTemplate : {
17071 cls: 'modal fade roo-combobox-touch-view',
17075 cls: 'modal-dialog',
17076 style : 'position:fixed', // we have to fix position....
17080 cls: 'modal-content',
17082 Roo.bootstrap.ComboBox.header,
17083 Roo.bootstrap.ComboBox.body,
17084 Roo.bootstrap.ComboBox.footer
17093 * Ext JS Library 1.1.1
17094 * Copyright(c) 2006-2007, Ext JS, LLC.
17096 * Originally Released Under LGPL - original licence link has changed is not relivant.
17099 * <script type="text/javascript">
17104 * @extends Roo.util.Observable
17105 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17106 * This class also supports single and multi selection modes. <br>
17107 * Create a data model bound view:
17109 var store = new Roo.data.Store(...);
17111 var view = new Roo.View({
17113 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17115 singleSelect: true,
17116 selectedClass: "ydataview-selected",
17120 // listen for node click?
17121 view.on("click", function(vw, index, node, e){
17122 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17126 dataModel.load("foobar.xml");
17128 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17130 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17131 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17133 * Note: old style constructor is still suported (container, template, config)
17136 * Create a new View
17137 * @param {Object} config The config object
17140 Roo.View = function(config, depreciated_tpl, depreciated_config){
17142 this.parent = false;
17144 if (typeof(depreciated_tpl) == 'undefined') {
17145 // new way.. - universal constructor.
17146 Roo.apply(this, config);
17147 this.el = Roo.get(this.el);
17150 this.el = Roo.get(config);
17151 this.tpl = depreciated_tpl;
17152 Roo.apply(this, depreciated_config);
17154 this.wrapEl = this.el.wrap().wrap();
17155 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17158 if(typeof(this.tpl) == "string"){
17159 this.tpl = new Roo.Template(this.tpl);
17161 // support xtype ctors..
17162 this.tpl = new Roo.factory(this.tpl, Roo);
17166 this.tpl.compile();
17171 * @event beforeclick
17172 * Fires before a click is processed. Returns false to cancel the default action.
17173 * @param {Roo.View} this
17174 * @param {Number} index The index of the target node
17175 * @param {HTMLElement} node The target node
17176 * @param {Roo.EventObject} e The raw event object
17178 "beforeclick" : true,
17181 * Fires when a template node is clicked.
17182 * @param {Roo.View} this
17183 * @param {Number} index The index of the target node
17184 * @param {HTMLElement} node The target node
17185 * @param {Roo.EventObject} e The raw event object
17190 * Fires when a template node is double clicked.
17191 * @param {Roo.View} this
17192 * @param {Number} index The index of the target node
17193 * @param {HTMLElement} node The target node
17194 * @param {Roo.EventObject} e The raw event object
17198 * @event contextmenu
17199 * Fires when a template node is right clicked.
17200 * @param {Roo.View} this
17201 * @param {Number} index The index of the target node
17202 * @param {HTMLElement} node The target node
17203 * @param {Roo.EventObject} e The raw event object
17205 "contextmenu" : true,
17207 * @event selectionchange
17208 * Fires when the selected nodes change.
17209 * @param {Roo.View} this
17210 * @param {Array} selections Array of the selected nodes
17212 "selectionchange" : true,
17215 * @event beforeselect
17216 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17217 * @param {Roo.View} this
17218 * @param {HTMLElement} node The node to be selected
17219 * @param {Array} selections Array of currently selected nodes
17221 "beforeselect" : true,
17223 * @event preparedata
17224 * Fires on every row to render, to allow you to change the data.
17225 * @param {Roo.View} this
17226 * @param {Object} data to be rendered (change this)
17228 "preparedata" : true
17236 "click": this.onClick,
17237 "dblclick": this.onDblClick,
17238 "contextmenu": this.onContextMenu,
17242 this.selections = [];
17244 this.cmp = new Roo.CompositeElementLite([]);
17246 this.store = Roo.factory(this.store, Roo.data);
17247 this.setStore(this.store, true);
17250 if ( this.footer && this.footer.xtype) {
17252 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17254 this.footer.dataSource = this.store;
17255 this.footer.container = fctr;
17256 this.footer = Roo.factory(this.footer, Roo);
17257 fctr.insertFirst(this.el);
17259 // this is a bit insane - as the paging toolbar seems to detach the el..
17260 // dom.parentNode.parentNode.parentNode
17261 // they get detached?
17265 Roo.View.superclass.constructor.call(this);
17270 Roo.extend(Roo.View, Roo.util.Observable, {
17273 * @cfg {Roo.data.Store} store Data store to load data from.
17278 * @cfg {String|Roo.Element} el The container element.
17283 * @cfg {String|Roo.Template} tpl The template used by this View
17287 * @cfg {String} dataName the named area of the template to use as the data area
17288 * Works with domtemplates roo-name="name"
17292 * @cfg {String} selectedClass The css class to add to selected nodes
17294 selectedClass : "x-view-selected",
17296 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17301 * @cfg {String} text to display on mask (default Loading)
17305 * @cfg {Boolean} multiSelect Allow multiple selection
17307 multiSelect : false,
17309 * @cfg {Boolean} singleSelect Allow single selection
17311 singleSelect: false,
17314 * @cfg {Boolean} toggleSelect - selecting
17316 toggleSelect : false,
17319 * @cfg {Boolean} tickable - selecting
17324 * Returns the element this view is bound to.
17325 * @return {Roo.Element}
17327 getEl : function(){
17328 return this.wrapEl;
17334 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17336 refresh : function(){
17337 //Roo.log('refresh');
17340 // if we are using something like 'domtemplate', then
17341 // the what gets used is:
17342 // t.applySubtemplate(NAME, data, wrapping data..)
17343 // the outer template then get' applied with
17344 // the store 'extra data'
17345 // and the body get's added to the
17346 // roo-name="data" node?
17347 // <span class='roo-tpl-{name}'></span> ?????
17351 this.clearSelections();
17352 this.el.update("");
17354 var records = this.store.getRange();
17355 if(records.length < 1) {
17357 // is this valid?? = should it render a template??
17359 this.el.update(this.emptyText);
17363 if (this.dataName) {
17364 this.el.update(t.apply(this.store.meta)); //????
17365 el = this.el.child('.roo-tpl-' + this.dataName);
17368 for(var i = 0, len = records.length; i < len; i++){
17369 var data = this.prepareData(records[i].data, i, records[i]);
17370 this.fireEvent("preparedata", this, data, i, records[i]);
17372 var d = Roo.apply({}, data);
17375 Roo.apply(d, {'roo-id' : Roo.id()});
17379 Roo.each(this.parent.item, function(item){
17380 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17383 Roo.apply(d, {'roo-data-checked' : 'checked'});
17387 html[html.length] = Roo.util.Format.trim(
17389 t.applySubtemplate(this.dataName, d, this.store.meta) :
17396 el.update(html.join(""));
17397 this.nodes = el.dom.childNodes;
17398 this.updateIndexes(0);
17403 * Function to override to reformat the data that is sent to
17404 * the template for each node.
17405 * DEPRICATED - use the preparedata event handler.
17406 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17407 * a JSON object for an UpdateManager bound view).
17409 prepareData : function(data, index, record)
17411 this.fireEvent("preparedata", this, data, index, record);
17415 onUpdate : function(ds, record){
17416 // Roo.log('on update');
17417 this.clearSelections();
17418 var index = this.store.indexOf(record);
17419 var n = this.nodes[index];
17420 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17421 n.parentNode.removeChild(n);
17422 this.updateIndexes(index, index);
17428 onAdd : function(ds, records, index)
17430 //Roo.log(['on Add', ds, records, index] );
17431 this.clearSelections();
17432 if(this.nodes.length == 0){
17436 var n = this.nodes[index];
17437 for(var i = 0, len = records.length; i < len; i++){
17438 var d = this.prepareData(records[i].data, i, records[i]);
17440 this.tpl.insertBefore(n, d);
17443 this.tpl.append(this.el, d);
17446 this.updateIndexes(index);
17449 onRemove : function(ds, record, index){
17450 // Roo.log('onRemove');
17451 this.clearSelections();
17452 var el = this.dataName ?
17453 this.el.child('.roo-tpl-' + this.dataName) :
17456 el.dom.removeChild(this.nodes[index]);
17457 this.updateIndexes(index);
17461 * Refresh an individual node.
17462 * @param {Number} index
17464 refreshNode : function(index){
17465 this.onUpdate(this.store, this.store.getAt(index));
17468 updateIndexes : function(startIndex, endIndex){
17469 var ns = this.nodes;
17470 startIndex = startIndex || 0;
17471 endIndex = endIndex || ns.length - 1;
17472 for(var i = startIndex; i <= endIndex; i++){
17473 ns[i].nodeIndex = i;
17478 * Changes the data store this view uses and refresh the view.
17479 * @param {Store} store
17481 setStore : function(store, initial){
17482 if(!initial && this.store){
17483 this.store.un("datachanged", this.refresh);
17484 this.store.un("add", this.onAdd);
17485 this.store.un("remove", this.onRemove);
17486 this.store.un("update", this.onUpdate);
17487 this.store.un("clear", this.refresh);
17488 this.store.un("beforeload", this.onBeforeLoad);
17489 this.store.un("load", this.onLoad);
17490 this.store.un("loadexception", this.onLoad);
17494 store.on("datachanged", this.refresh, this);
17495 store.on("add", this.onAdd, this);
17496 store.on("remove", this.onRemove, this);
17497 store.on("update", this.onUpdate, this);
17498 store.on("clear", this.refresh, this);
17499 store.on("beforeload", this.onBeforeLoad, this);
17500 store.on("load", this.onLoad, this);
17501 store.on("loadexception", this.onLoad, this);
17509 * onbeforeLoad - masks the loading area.
17512 onBeforeLoad : function(store,opts)
17514 //Roo.log('onBeforeLoad');
17516 this.el.update("");
17518 this.el.mask(this.mask ? this.mask : "Loading" );
17520 onLoad : function ()
17527 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17528 * @param {HTMLElement} node
17529 * @return {HTMLElement} The template node
17531 findItemFromChild : function(node){
17532 var el = this.dataName ?
17533 this.el.child('.roo-tpl-' + this.dataName,true) :
17536 if(!node || node.parentNode == el){
17539 var p = node.parentNode;
17540 while(p && p != el){
17541 if(p.parentNode == el){
17550 onClick : function(e){
17551 var item = this.findItemFromChild(e.getTarget());
17553 var index = this.indexOf(item);
17554 if(this.onItemClick(item, index, e) !== false){
17555 this.fireEvent("click", this, index, item, e);
17558 this.clearSelections();
17563 onContextMenu : function(e){
17564 var item = this.findItemFromChild(e.getTarget());
17566 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17571 onDblClick : function(e){
17572 var item = this.findItemFromChild(e.getTarget());
17574 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17578 onItemClick : function(item, index, e)
17580 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17583 if (this.toggleSelect) {
17584 var m = this.isSelected(item) ? 'unselect' : 'select';
17587 _t[m](item, true, false);
17590 if(this.multiSelect || this.singleSelect){
17591 if(this.multiSelect && e.shiftKey && this.lastSelection){
17592 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17594 this.select(item, this.multiSelect && e.ctrlKey);
17595 this.lastSelection = item;
17598 if(!this.tickable){
17599 e.preventDefault();
17607 * Get the number of selected nodes.
17610 getSelectionCount : function(){
17611 return this.selections.length;
17615 * Get the currently selected nodes.
17616 * @return {Array} An array of HTMLElements
17618 getSelectedNodes : function(){
17619 return this.selections;
17623 * Get the indexes of the selected nodes.
17626 getSelectedIndexes : function(){
17627 var indexes = [], s = this.selections;
17628 for(var i = 0, len = s.length; i < len; i++){
17629 indexes.push(s[i].nodeIndex);
17635 * Clear all selections
17636 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17638 clearSelections : function(suppressEvent){
17639 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17640 this.cmp.elements = this.selections;
17641 this.cmp.removeClass(this.selectedClass);
17642 this.selections = [];
17643 if(!suppressEvent){
17644 this.fireEvent("selectionchange", this, this.selections);
17650 * Returns true if the passed node is selected
17651 * @param {HTMLElement/Number} node The node or node index
17652 * @return {Boolean}
17654 isSelected : function(node){
17655 var s = this.selections;
17659 node = this.getNode(node);
17660 return s.indexOf(node) !== -1;
17665 * @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
17666 * @param {Boolean} keepExisting (optional) true to keep existing selections
17667 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17669 select : function(nodeInfo, keepExisting, suppressEvent){
17670 if(nodeInfo instanceof Array){
17672 this.clearSelections(true);
17674 for(var i = 0, len = nodeInfo.length; i < len; i++){
17675 this.select(nodeInfo[i], true, true);
17679 var node = this.getNode(nodeInfo);
17680 if(!node || this.isSelected(node)){
17681 return; // already selected.
17684 this.clearSelections(true);
17687 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17688 Roo.fly(node).addClass(this.selectedClass);
17689 this.selections.push(node);
17690 if(!suppressEvent){
17691 this.fireEvent("selectionchange", this, this.selections);
17699 * @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
17700 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17701 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17703 unselect : function(nodeInfo, keepExisting, suppressEvent)
17705 if(nodeInfo instanceof Array){
17706 Roo.each(this.selections, function(s) {
17707 this.unselect(s, nodeInfo);
17711 var node = this.getNode(nodeInfo);
17712 if(!node || !this.isSelected(node)){
17713 //Roo.log("not selected");
17714 return; // not selected.
17718 Roo.each(this.selections, function(s) {
17720 Roo.fly(node).removeClass(this.selectedClass);
17727 this.selections= ns;
17728 this.fireEvent("selectionchange", this, this.selections);
17732 * Gets a template node.
17733 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17734 * @return {HTMLElement} The node or null if it wasn't found
17736 getNode : function(nodeInfo){
17737 if(typeof nodeInfo == "string"){
17738 return document.getElementById(nodeInfo);
17739 }else if(typeof nodeInfo == "number"){
17740 return this.nodes[nodeInfo];
17746 * Gets a range template nodes.
17747 * @param {Number} startIndex
17748 * @param {Number} endIndex
17749 * @return {Array} An array of nodes
17751 getNodes : function(start, end){
17752 var ns = this.nodes;
17753 start = start || 0;
17754 end = typeof end == "undefined" ? ns.length - 1 : end;
17757 for(var i = start; i <= end; i++){
17761 for(var i = start; i >= end; i--){
17769 * Finds the index of the passed node
17770 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17771 * @return {Number} The index of the node or -1
17773 indexOf : function(node){
17774 node = this.getNode(node);
17775 if(typeof node.nodeIndex == "number"){
17776 return node.nodeIndex;
17778 var ns = this.nodes;
17779 for(var i = 0, len = ns.length; i < len; i++){
17790 * based on jquery fullcalendar
17794 Roo.bootstrap = Roo.bootstrap || {};
17796 * @class Roo.bootstrap.Calendar
17797 * @extends Roo.bootstrap.Component
17798 * Bootstrap Calendar class
17799 * @cfg {Boolean} loadMask (true|false) default false
17800 * @cfg {Object} header generate the user specific header of the calendar, default false
17803 * Create a new Container
17804 * @param {Object} config The config object
17809 Roo.bootstrap.Calendar = function(config){
17810 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17814 * Fires when a date is selected
17815 * @param {DatePicker} this
17816 * @param {Date} date The selected date
17820 * @event monthchange
17821 * Fires when the displayed month changes
17822 * @param {DatePicker} this
17823 * @param {Date} date The selected month
17825 'monthchange': true,
17827 * @event evententer
17828 * Fires when mouse over an event
17829 * @param {Calendar} this
17830 * @param {event} Event
17832 'evententer': true,
17834 * @event eventleave
17835 * Fires when the mouse leaves an
17836 * @param {Calendar} this
17839 'eventleave': true,
17841 * @event eventclick
17842 * Fires when the mouse click an
17843 * @param {Calendar} this
17852 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17855 * @cfg {Number} startDay
17856 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17864 getAutoCreate : function(){
17867 var fc_button = function(name, corner, style, content ) {
17868 return Roo.apply({},{
17870 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17872 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17875 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17886 style : 'width:100%',
17893 cls : 'fc-header-left',
17895 fc_button('prev', 'left', 'arrow', '‹' ),
17896 fc_button('next', 'right', 'arrow', '›' ),
17897 { tag: 'span', cls: 'fc-header-space' },
17898 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17906 cls : 'fc-header-center',
17910 cls: 'fc-header-title',
17913 html : 'month / year'
17921 cls : 'fc-header-right',
17923 /* fc_button('month', 'left', '', 'month' ),
17924 fc_button('week', '', '', 'week' ),
17925 fc_button('day', 'right', '', 'day' )
17937 header = this.header;
17940 var cal_heads = function() {
17942 // fixme - handle this.
17944 for (var i =0; i < Date.dayNames.length; i++) {
17945 var d = Date.dayNames[i];
17948 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17949 html : d.substring(0,3)
17953 ret[0].cls += ' fc-first';
17954 ret[6].cls += ' fc-last';
17957 var cal_cell = function(n) {
17960 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17965 cls: 'fc-day-number',
17969 cls: 'fc-day-content',
17973 style: 'position: relative;' // height: 17px;
17985 var cal_rows = function() {
17988 for (var r = 0; r < 6; r++) {
17995 for (var i =0; i < Date.dayNames.length; i++) {
17996 var d = Date.dayNames[i];
17997 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18000 row.cn[0].cls+=' fc-first';
18001 row.cn[0].cn[0].style = 'min-height:90px';
18002 row.cn[6].cls+=' fc-last';
18006 ret[0].cls += ' fc-first';
18007 ret[4].cls += ' fc-prev-last';
18008 ret[5].cls += ' fc-last';
18015 cls: 'fc-border-separate',
18016 style : 'width:100%',
18024 cls : 'fc-first fc-last',
18042 cls : 'fc-content',
18043 style : "position: relative;",
18046 cls : 'fc-view fc-view-month fc-grid',
18047 style : 'position: relative',
18048 unselectable : 'on',
18051 cls : 'fc-event-container',
18052 style : 'position:absolute;z-index:8;top:0;left:0;'
18070 initEvents : function()
18073 throw "can not find store for calendar";
18079 style: "text-align:center",
18083 style: "background-color:white;width:50%;margin:250 auto",
18087 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18098 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18100 var size = this.el.select('.fc-content', true).first().getSize();
18101 this.maskEl.setSize(size.width, size.height);
18102 this.maskEl.enableDisplayMode("block");
18103 if(!this.loadMask){
18104 this.maskEl.hide();
18107 this.store = Roo.factory(this.store, Roo.data);
18108 this.store.on('load', this.onLoad, this);
18109 this.store.on('beforeload', this.onBeforeLoad, this);
18113 this.cells = this.el.select('.fc-day',true);
18114 //Roo.log(this.cells);
18115 this.textNodes = this.el.query('.fc-day-number');
18116 this.cells.addClassOnOver('fc-state-hover');
18118 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18119 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18120 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18121 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18123 this.on('monthchange', this.onMonthChange, this);
18125 this.update(new Date().clearTime());
18128 resize : function() {
18129 var sz = this.el.getSize();
18131 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18132 this.el.select('.fc-day-content div',true).setHeight(34);
18137 showPrevMonth : function(e){
18138 this.update(this.activeDate.add("mo", -1));
18140 showToday : function(e){
18141 this.update(new Date().clearTime());
18144 showNextMonth : function(e){
18145 this.update(this.activeDate.add("mo", 1));
18149 showPrevYear : function(){
18150 this.update(this.activeDate.add("y", -1));
18154 showNextYear : function(){
18155 this.update(this.activeDate.add("y", 1));
18160 update : function(date)
18162 var vd = this.activeDate;
18163 this.activeDate = date;
18164 // if(vd && this.el){
18165 // var t = date.getTime();
18166 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18167 // Roo.log('using add remove');
18169 // this.fireEvent('monthchange', this, date);
18171 // this.cells.removeClass("fc-state-highlight");
18172 // this.cells.each(function(c){
18173 // if(c.dateValue == t){
18174 // c.addClass("fc-state-highlight");
18175 // setTimeout(function(){
18176 // try{c.dom.firstChild.focus();}catch(e){}
18186 var days = date.getDaysInMonth();
18188 var firstOfMonth = date.getFirstDateOfMonth();
18189 var startingPos = firstOfMonth.getDay()-this.startDay;
18191 if(startingPos < this.startDay){
18195 var pm = date.add(Date.MONTH, -1);
18196 var prevStart = pm.getDaysInMonth()-startingPos;
18198 this.cells = this.el.select('.fc-day',true);
18199 this.textNodes = this.el.query('.fc-day-number');
18200 this.cells.addClassOnOver('fc-state-hover');
18202 var cells = this.cells.elements;
18203 var textEls = this.textNodes;
18205 Roo.each(cells, function(cell){
18206 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18209 days += startingPos;
18211 // convert everything to numbers so it's fast
18212 var day = 86400000;
18213 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18216 //Roo.log(prevStart);
18218 var today = new Date().clearTime().getTime();
18219 var sel = date.clearTime().getTime();
18220 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18221 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18222 var ddMatch = this.disabledDatesRE;
18223 var ddText = this.disabledDatesText;
18224 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18225 var ddaysText = this.disabledDaysText;
18226 var format = this.format;
18228 var setCellClass = function(cal, cell){
18232 //Roo.log('set Cell Class');
18234 var t = d.getTime();
18238 cell.dateValue = t;
18240 cell.className += " fc-today";
18241 cell.className += " fc-state-highlight";
18242 cell.title = cal.todayText;
18245 // disable highlight in other month..
18246 //cell.className += " fc-state-highlight";
18251 cell.className = " fc-state-disabled";
18252 cell.title = cal.minText;
18256 cell.className = " fc-state-disabled";
18257 cell.title = cal.maxText;
18261 if(ddays.indexOf(d.getDay()) != -1){
18262 cell.title = ddaysText;
18263 cell.className = " fc-state-disabled";
18266 if(ddMatch && format){
18267 var fvalue = d.dateFormat(format);
18268 if(ddMatch.test(fvalue)){
18269 cell.title = ddText.replace("%0", fvalue);
18270 cell.className = " fc-state-disabled";
18274 if (!cell.initialClassName) {
18275 cell.initialClassName = cell.dom.className;
18278 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18283 for(; i < startingPos; i++) {
18284 textEls[i].innerHTML = (++prevStart);
18285 d.setDate(d.getDate()+1);
18287 cells[i].className = "fc-past fc-other-month";
18288 setCellClass(this, cells[i]);
18293 for(; i < days; i++){
18294 intDay = i - startingPos + 1;
18295 textEls[i].innerHTML = (intDay);
18296 d.setDate(d.getDate()+1);
18298 cells[i].className = ''; // "x-date-active";
18299 setCellClass(this, cells[i]);
18303 for(; i < 42; i++) {
18304 textEls[i].innerHTML = (++extraDays);
18305 d.setDate(d.getDate()+1);
18307 cells[i].className = "fc-future fc-other-month";
18308 setCellClass(this, cells[i]);
18311 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18313 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18315 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18316 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18318 if(totalRows != 6){
18319 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18320 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18323 this.fireEvent('monthchange', this, date);
18327 if(!this.internalRender){
18328 var main = this.el.dom.firstChild;
18329 var w = main.offsetWidth;
18330 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18331 Roo.fly(main).setWidth(w);
18332 this.internalRender = true;
18333 // opera does not respect the auto grow header center column
18334 // then, after it gets a width opera refuses to recalculate
18335 // without a second pass
18336 if(Roo.isOpera && !this.secondPass){
18337 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18338 this.secondPass = true;
18339 this.update.defer(10, this, [date]);
18346 findCell : function(dt) {
18347 dt = dt.clearTime().getTime();
18349 this.cells.each(function(c){
18350 //Roo.log("check " +c.dateValue + '?=' + dt);
18351 if(c.dateValue == dt){
18361 findCells : function(ev) {
18362 var s = ev.start.clone().clearTime().getTime();
18364 var e= ev.end.clone().clearTime().getTime();
18367 this.cells.each(function(c){
18368 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18370 if(c.dateValue > e){
18373 if(c.dateValue < s){
18382 // findBestRow: function(cells)
18386 // for (var i =0 ; i < cells.length;i++) {
18387 // ret = Math.max(cells[i].rows || 0,ret);
18394 addItem : function(ev)
18396 // look for vertical location slot in
18397 var cells = this.findCells(ev);
18399 // ev.row = this.findBestRow(cells);
18401 // work out the location.
18405 for(var i =0; i < cells.length; i++) {
18407 cells[i].row = cells[0].row;
18410 cells[i].row = cells[i].row + 1;
18420 if (crow.start.getY() == cells[i].getY()) {
18422 crow.end = cells[i];
18439 cells[0].events.push(ev);
18441 this.calevents.push(ev);
18444 clearEvents: function() {
18446 if(!this.calevents){
18450 Roo.each(this.cells.elements, function(c){
18456 Roo.each(this.calevents, function(e) {
18457 Roo.each(e.els, function(el) {
18458 el.un('mouseenter' ,this.onEventEnter, this);
18459 el.un('mouseleave' ,this.onEventLeave, this);
18464 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18470 renderEvents: function()
18474 this.cells.each(function(c) {
18483 if(c.row != c.events.length){
18484 r = 4 - (4 - (c.row - c.events.length));
18487 c.events = ev.slice(0, r);
18488 c.more = ev.slice(r);
18490 if(c.more.length && c.more.length == 1){
18491 c.events.push(c.more.pop());
18494 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18498 this.cells.each(function(c) {
18500 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18503 for (var e = 0; e < c.events.length; e++){
18504 var ev = c.events[e];
18505 var rows = ev.rows;
18507 for(var i = 0; i < rows.length; i++) {
18509 // how many rows should it span..
18512 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18513 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18515 unselectable : "on",
18518 cls: 'fc-event-inner',
18522 // cls: 'fc-event-time',
18523 // html : cells.length > 1 ? '' : ev.time
18527 cls: 'fc-event-title',
18528 html : String.format('{0}', ev.title)
18535 cls: 'ui-resizable-handle ui-resizable-e',
18536 html : '  '
18543 cfg.cls += ' fc-event-start';
18545 if ((i+1) == rows.length) {
18546 cfg.cls += ' fc-event-end';
18549 var ctr = _this.el.select('.fc-event-container',true).first();
18550 var cg = ctr.createChild(cfg);
18552 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18553 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18555 var r = (c.more.length) ? 1 : 0;
18556 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18557 cg.setWidth(ebox.right - sbox.x -2);
18559 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18560 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18561 cg.on('click', _this.onEventClick, _this, ev);
18572 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18573 style : 'position: absolute',
18574 unselectable : "on",
18577 cls: 'fc-event-inner',
18581 cls: 'fc-event-title',
18589 cls: 'ui-resizable-handle ui-resizable-e',
18590 html : '  '
18596 var ctr = _this.el.select('.fc-event-container',true).first();
18597 var cg = ctr.createChild(cfg);
18599 var sbox = c.select('.fc-day-content',true).first().getBox();
18600 var ebox = c.select('.fc-day-content',true).first().getBox();
18602 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18603 cg.setWidth(ebox.right - sbox.x -2);
18605 cg.on('click', _this.onMoreEventClick, _this, c.more);
18615 onEventEnter: function (e, el,event,d) {
18616 this.fireEvent('evententer', this, el, event);
18619 onEventLeave: function (e, el,event,d) {
18620 this.fireEvent('eventleave', this, el, event);
18623 onEventClick: function (e, el,event,d) {
18624 this.fireEvent('eventclick', this, el, event);
18627 onMonthChange: function () {
18631 onMoreEventClick: function(e, el, more)
18635 this.calpopover.placement = 'right';
18636 this.calpopover.setTitle('More');
18638 this.calpopover.setContent('');
18640 var ctr = this.calpopover.el.select('.popover-content', true).first();
18642 Roo.each(more, function(m){
18644 cls : 'fc-event-hori fc-event-draggable',
18647 var cg = ctr.createChild(cfg);
18649 cg.on('click', _this.onEventClick, _this, m);
18652 this.calpopover.show(el);
18657 onLoad: function ()
18659 this.calevents = [];
18662 if(this.store.getCount() > 0){
18663 this.store.data.each(function(d){
18666 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18667 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18668 time : d.data.start_time,
18669 title : d.data.title,
18670 description : d.data.description,
18671 venue : d.data.venue
18676 this.renderEvents();
18678 if(this.calevents.length && this.loadMask){
18679 this.maskEl.hide();
18683 onBeforeLoad: function()
18685 this.clearEvents();
18687 this.maskEl.show();
18701 * @class Roo.bootstrap.Popover
18702 * @extends Roo.bootstrap.Component
18703 * Bootstrap Popover class
18704 * @cfg {String} html contents of the popover (or false to use children..)
18705 * @cfg {String} title of popover (or false to hide)
18706 * @cfg {String} placement how it is placed
18707 * @cfg {String} trigger click || hover (or false to trigger manually)
18708 * @cfg {String} over what (parent or false to trigger manually.)
18709 * @cfg {Number} delay - delay before showing
18712 * Create a new Popover
18713 * @param {Object} config The config object
18716 Roo.bootstrap.Popover = function(config){
18717 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18723 * After the popover show
18725 * @param {Roo.bootstrap.Popover} this
18730 * After the popover hide
18732 * @param {Roo.bootstrap.Popover} this
18738 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18740 title: 'Fill in a title',
18743 placement : 'right',
18744 trigger : 'hover', // hover
18750 can_build_overlaid : false,
18752 getChildContainer : function()
18754 return this.el.select('.popover-content',true).first();
18757 getAutoCreate : function(){
18760 cls : 'popover roo-dynamic',
18761 style: 'display:block',
18767 cls : 'popover-inner',
18771 cls: 'popover-title popover-header',
18775 cls : 'popover-content popover-body',
18786 setTitle: function(str)
18789 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18791 setContent: function(str)
18794 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18796 // as it get's added to the bottom of the page.
18797 onRender : function(ct, position)
18799 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18801 var cfg = Roo.apply({}, this.getAutoCreate());
18805 cfg.cls += ' ' + this.cls;
18808 cfg.style = this.style;
18810 //Roo.log("adding to ");
18811 this.el = Roo.get(document.body).createChild(cfg, position);
18812 // Roo.log(this.el);
18817 initEvents : function()
18819 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18820 this.el.enableDisplayMode('block');
18822 if (this.over === false) {
18825 if (this.triggers === false) {
18828 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18829 var triggers = this.trigger ? this.trigger.split(' ') : [];
18830 Roo.each(triggers, function(trigger) {
18832 if (trigger == 'click') {
18833 on_el.on('click', this.toggle, this);
18834 } else if (trigger != 'manual') {
18835 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18836 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18838 on_el.on(eventIn ,this.enter, this);
18839 on_el.on(eventOut, this.leave, this);
18850 toggle : function () {
18851 this.hoverState == 'in' ? this.leave() : this.enter();
18854 enter : function () {
18856 clearTimeout(this.timeout);
18858 this.hoverState = 'in';
18860 if (!this.delay || !this.delay.show) {
18865 this.timeout = setTimeout(function () {
18866 if (_t.hoverState == 'in') {
18869 }, this.delay.show)
18872 leave : function() {
18873 clearTimeout(this.timeout);
18875 this.hoverState = 'out';
18877 if (!this.delay || !this.delay.hide) {
18882 this.timeout = setTimeout(function () {
18883 if (_t.hoverState == 'out') {
18886 }, this.delay.hide)
18889 show : function (on_el)
18892 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18896 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18897 if (this.html !== false) {
18898 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18900 this.el.removeClass([
18901 'fade','top','bottom', 'left', 'right','in',
18902 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18904 if (!this.title.length) {
18905 this.el.select('.popover-title',true).hide();
18908 var placement = typeof this.placement == 'function' ?
18909 this.placement.call(this, this.el, on_el) :
18912 var autoToken = /\s?auto?\s?/i;
18913 var autoPlace = autoToken.test(placement);
18915 placement = placement.replace(autoToken, '') || 'top';
18919 //this.el.setXY([0,0]);
18921 this.el.dom.style.display='block';
18922 this.el.addClass(placement);
18924 //this.el.appendTo(on_el);
18926 var p = this.getPosition();
18927 var box = this.el.getBox();
18932 var align = Roo.bootstrap.Popover.alignment[placement];
18935 this.el.alignTo(on_el, align[0],align[1]);
18936 //var arrow = this.el.select('.arrow',true).first();
18937 //arrow.set(align[2],
18939 this.el.addClass('in');
18942 if (this.el.hasClass('fade')) {
18946 this.hoverState = 'in';
18948 this.fireEvent('show', this);
18953 this.el.setXY([0,0]);
18954 this.el.removeClass('in');
18956 this.hoverState = null;
18958 this.fireEvent('hide', this);
18963 Roo.bootstrap.Popover.alignment = {
18964 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18965 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18966 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18967 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18978 * @class Roo.bootstrap.Progress
18979 * @extends Roo.bootstrap.Component
18980 * Bootstrap Progress class
18981 * @cfg {Boolean} striped striped of the progress bar
18982 * @cfg {Boolean} active animated of the progress bar
18986 * Create a new Progress
18987 * @param {Object} config The config object
18990 Roo.bootstrap.Progress = function(config){
18991 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18994 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18999 getAutoCreate : function(){
19007 cfg.cls += ' progress-striped';
19011 cfg.cls += ' active';
19030 * @class Roo.bootstrap.ProgressBar
19031 * @extends Roo.bootstrap.Component
19032 * Bootstrap ProgressBar class
19033 * @cfg {Number} aria_valuenow aria-value now
19034 * @cfg {Number} aria_valuemin aria-value min
19035 * @cfg {Number} aria_valuemax aria-value max
19036 * @cfg {String} label label for the progress bar
19037 * @cfg {String} panel (success | info | warning | danger )
19038 * @cfg {String} role role of the progress bar
19039 * @cfg {String} sr_only text
19043 * Create a new ProgressBar
19044 * @param {Object} config The config object
19047 Roo.bootstrap.ProgressBar = function(config){
19048 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19051 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19055 aria_valuemax : 100,
19061 getAutoCreate : function()
19066 cls: 'progress-bar',
19067 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19079 cfg.role = this.role;
19082 if(this.aria_valuenow){
19083 cfg['aria-valuenow'] = this.aria_valuenow;
19086 if(this.aria_valuemin){
19087 cfg['aria-valuemin'] = this.aria_valuemin;
19090 if(this.aria_valuemax){
19091 cfg['aria-valuemax'] = this.aria_valuemax;
19094 if(this.label && !this.sr_only){
19095 cfg.html = this.label;
19099 cfg.cls += ' progress-bar-' + this.panel;
19105 update : function(aria_valuenow)
19107 this.aria_valuenow = aria_valuenow;
19109 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19124 * @class Roo.bootstrap.TabGroup
19125 * @extends Roo.bootstrap.Column
19126 * Bootstrap Column class
19127 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19128 * @cfg {Boolean} carousel true to make the group behave like a carousel
19129 * @cfg {Boolean} bullets show bullets for the panels
19130 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19131 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19132 * @cfg {Boolean} showarrow (true|false) show arrow default true
19135 * Create a new TabGroup
19136 * @param {Object} config The config object
19139 Roo.bootstrap.TabGroup = function(config){
19140 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19142 this.navId = Roo.id();
19145 Roo.bootstrap.TabGroup.register(this);
19149 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19152 transition : false,
19157 slideOnTouch : false,
19160 getAutoCreate : function()
19162 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19164 cfg.cls += ' tab-content';
19166 if (this.carousel) {
19167 cfg.cls += ' carousel slide';
19170 cls : 'carousel-inner',
19174 if(this.bullets && !Roo.isTouch){
19177 cls : 'carousel-bullets',
19181 if(this.bullets_cls){
19182 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19189 cfg.cn[0].cn.push(bullets);
19192 if(this.showarrow){
19193 cfg.cn[0].cn.push({
19195 class : 'carousel-arrow',
19199 class : 'carousel-prev',
19203 class : 'fa fa-chevron-left'
19209 class : 'carousel-next',
19213 class : 'fa fa-chevron-right'
19226 initEvents: function()
19228 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19229 // this.el.on("touchstart", this.onTouchStart, this);
19232 if(this.autoslide){
19235 this.slideFn = window.setInterval(function() {
19236 _this.showPanelNext();
19240 if(this.showarrow){
19241 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19242 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19248 // onTouchStart : function(e, el, o)
19250 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19254 // this.showPanelNext();
19258 getChildContainer : function()
19260 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19264 * register a Navigation item
19265 * @param {Roo.bootstrap.NavItem} the navitem to add
19267 register : function(item)
19269 this.tabs.push( item);
19270 item.navId = this.navId; // not really needed..
19275 getActivePanel : function()
19278 Roo.each(this.tabs, function(t) {
19288 getPanelByName : function(n)
19291 Roo.each(this.tabs, function(t) {
19292 if (t.tabId == n) {
19300 indexOfPanel : function(p)
19303 Roo.each(this.tabs, function(t,i) {
19304 if (t.tabId == p.tabId) {
19313 * show a specific panel
19314 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19315 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19317 showPanel : function (pan)
19319 if(this.transition || typeof(pan) == 'undefined'){
19320 Roo.log("waiting for the transitionend");
19324 if (typeof(pan) == 'number') {
19325 pan = this.tabs[pan];
19328 if (typeof(pan) == 'string') {
19329 pan = this.getPanelByName(pan);
19332 var cur = this.getActivePanel();
19335 Roo.log('pan or acitve pan is undefined');
19339 if (pan.tabId == this.getActivePanel().tabId) {
19343 if (false === cur.fireEvent('beforedeactivate')) {
19347 if(this.bullets > 0 && !Roo.isTouch){
19348 this.setActiveBullet(this.indexOfPanel(pan));
19351 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19353 //class="carousel-item carousel-item-next carousel-item-left"
19355 this.transition = true;
19356 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
19357 var lr = dir == 'next' ? 'left' : 'right';
19358 pan.el.addClass(dir); // or prev
19359 pan.el.addClass('carousel-item-' + dir); // or prev
19360 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19361 cur.el.addClass(lr); // or right
19362 pan.el.addClass(lr);
19363 cur.el.addClass('carousel-item-' +lr); // or right
19364 pan.el.addClass('carousel-item-' +lr);
19368 cur.el.on('transitionend', function() {
19369 Roo.log("trans end?");
19371 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19372 pan.setActive(true);
19374 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19375 cur.setActive(false);
19377 _this.transition = false;
19379 }, this, { single: true } );
19384 cur.setActive(false);
19385 pan.setActive(true);
19390 showPanelNext : function()
19392 var i = this.indexOfPanel(this.getActivePanel());
19394 if (i >= this.tabs.length - 1 && !this.autoslide) {
19398 if (i >= this.tabs.length - 1 && this.autoslide) {
19402 this.showPanel(this.tabs[i+1]);
19405 showPanelPrev : function()
19407 var i = this.indexOfPanel(this.getActivePanel());
19409 if (i < 1 && !this.autoslide) {
19413 if (i < 1 && this.autoslide) {
19414 i = this.tabs.length;
19417 this.showPanel(this.tabs[i-1]);
19421 addBullet: function()
19423 if(!this.bullets || Roo.isTouch){
19426 var ctr = this.el.select('.carousel-bullets',true).first();
19427 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19428 var bullet = ctr.createChild({
19429 cls : 'bullet bullet-' + i
19430 },ctr.dom.lastChild);
19435 bullet.on('click', (function(e, el, o, ii, t){
19437 e.preventDefault();
19439 this.showPanel(ii);
19441 if(this.autoslide && this.slideFn){
19442 clearInterval(this.slideFn);
19443 this.slideFn = window.setInterval(function() {
19444 _this.showPanelNext();
19448 }).createDelegate(this, [i, bullet], true));
19453 setActiveBullet : function(i)
19459 Roo.each(this.el.select('.bullet', true).elements, function(el){
19460 el.removeClass('selected');
19463 var bullet = this.el.select('.bullet-' + i, true).first();
19469 bullet.addClass('selected');
19480 Roo.apply(Roo.bootstrap.TabGroup, {
19484 * register a Navigation Group
19485 * @param {Roo.bootstrap.NavGroup} the navgroup to add
19487 register : function(navgrp)
19489 this.groups[navgrp.navId] = navgrp;
19493 * fetch a Navigation Group based on the navigation ID
19494 * if one does not exist , it will get created.
19495 * @param {string} the navgroup to add
19496 * @returns {Roo.bootstrap.NavGroup} the navgroup
19498 get: function(navId) {
19499 if (typeof(this.groups[navId]) == 'undefined') {
19500 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19502 return this.groups[navId] ;
19517 * @class Roo.bootstrap.TabPanel
19518 * @extends Roo.bootstrap.Component
19519 * Bootstrap TabPanel class
19520 * @cfg {Boolean} active panel active
19521 * @cfg {String} html panel content
19522 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19523 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19524 * @cfg {String} href click to link..
19528 * Create a new TabPanel
19529 * @param {Object} config The config object
19532 Roo.bootstrap.TabPanel = function(config){
19533 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19537 * Fires when the active status changes
19538 * @param {Roo.bootstrap.TabPanel} this
19539 * @param {Boolean} state the new state
19544 * @event beforedeactivate
19545 * Fires before a tab is de-activated - can be used to do validation on a form.
19546 * @param {Roo.bootstrap.TabPanel} this
19547 * @return {Boolean} false if there is an error
19550 'beforedeactivate': true
19553 this.tabId = this.tabId || Roo.id();
19557 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19565 getAutoCreate : function(){
19570 // item is needed for carousel - not sure if it has any effect otherwise
19571 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19572 html: this.html || ''
19576 cfg.cls += ' active';
19580 cfg.tabId = this.tabId;
19588 initEvents: function()
19590 var p = this.parent();
19592 this.navId = this.navId || p.navId;
19594 if (typeof(this.navId) != 'undefined') {
19595 // not really needed.. but just in case.. parent should be a NavGroup.
19596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19600 var i = tg.tabs.length - 1;
19602 if(this.active && tg.bullets > 0 && i < tg.bullets){
19603 tg.setActiveBullet(i);
19607 this.el.on('click', this.onClick, this);
19610 this.el.on("touchstart", this.onTouchStart, this);
19611 this.el.on("touchmove", this.onTouchMove, this);
19612 this.el.on("touchend", this.onTouchEnd, this);
19617 onRender : function(ct, position)
19619 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19622 setActive : function(state)
19624 Roo.log("panel - set active " + this.tabId + "=" + state);
19626 this.active = state;
19628 this.el.removeClass('active');
19630 } else if (!this.el.hasClass('active')) {
19631 this.el.addClass('active');
19634 this.fireEvent('changed', this, state);
19637 onClick : function(e)
19639 e.preventDefault();
19641 if(!this.href.length){
19645 window.location.href = this.href;
19654 onTouchStart : function(e)
19656 this.swiping = false;
19658 this.startX = e.browserEvent.touches[0].clientX;
19659 this.startY = e.browserEvent.touches[0].clientY;
19662 onTouchMove : function(e)
19664 this.swiping = true;
19666 this.endX = e.browserEvent.touches[0].clientX;
19667 this.endY = e.browserEvent.touches[0].clientY;
19670 onTouchEnd : function(e)
19677 var tabGroup = this.parent();
19679 if(this.endX > this.startX){ // swiping right
19680 tabGroup.showPanelPrev();
19684 if(this.startX > this.endX){ // swiping left
19685 tabGroup.showPanelNext();
19704 * @class Roo.bootstrap.DateField
19705 * @extends Roo.bootstrap.Input
19706 * Bootstrap DateField class
19707 * @cfg {Number} weekStart default 0
19708 * @cfg {String} viewMode default empty, (months|years)
19709 * @cfg {String} minViewMode default empty, (months|years)
19710 * @cfg {Number} startDate default -Infinity
19711 * @cfg {Number} endDate default Infinity
19712 * @cfg {Boolean} todayHighlight default false
19713 * @cfg {Boolean} todayBtn default false
19714 * @cfg {Boolean} calendarWeeks default false
19715 * @cfg {Object} daysOfWeekDisabled default empty
19716 * @cfg {Boolean} singleMode default false (true | false)
19718 * @cfg {Boolean} keyboardNavigation default true
19719 * @cfg {String} language default en
19722 * Create a new DateField
19723 * @param {Object} config The config object
19726 Roo.bootstrap.DateField = function(config){
19727 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19731 * Fires when this field show.
19732 * @param {Roo.bootstrap.DateField} this
19733 * @param {Mixed} date The date value
19738 * Fires when this field hide.
19739 * @param {Roo.bootstrap.DateField} this
19740 * @param {Mixed} date The date value
19745 * Fires when select a date.
19746 * @param {Roo.bootstrap.DateField} this
19747 * @param {Mixed} date The date value
19751 * @event beforeselect
19752 * Fires when before select a date.
19753 * @param {Roo.bootstrap.DateField} this
19754 * @param {Mixed} date The date value
19756 beforeselect : true
19760 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19763 * @cfg {String} format
19764 * The default date format string which can be overriden for localization support. The format must be
19765 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19769 * @cfg {String} altFormats
19770 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19771 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19773 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19781 todayHighlight : false,
19787 keyboardNavigation: true,
19789 calendarWeeks: false,
19791 startDate: -Infinity,
19795 daysOfWeekDisabled: [],
19799 singleMode : false,
19801 UTCDate: function()
19803 return new Date(Date.UTC.apply(Date, arguments));
19806 UTCToday: function()
19808 var today = new Date();
19809 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19812 getDate: function() {
19813 var d = this.getUTCDate();
19814 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19817 getUTCDate: function() {
19821 setDate: function(d) {
19822 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19825 setUTCDate: function(d) {
19827 this.setValue(this.formatDate(this.date));
19830 onRender: function(ct, position)
19833 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19835 this.language = this.language || 'en';
19836 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19837 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19839 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19840 this.format = this.format || 'm/d/y';
19841 this.isInline = false;
19842 this.isInput = true;
19843 this.component = this.el.select('.add-on', true).first() || false;
19844 this.component = (this.component && this.component.length === 0) ? false : this.component;
19845 this.hasInput = this.component && this.inputEl().length;
19847 if (typeof(this.minViewMode === 'string')) {
19848 switch (this.minViewMode) {
19850 this.minViewMode = 1;
19853 this.minViewMode = 2;
19856 this.minViewMode = 0;
19861 if (typeof(this.viewMode === 'string')) {
19862 switch (this.viewMode) {
19875 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19877 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19879 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19881 this.picker().on('mousedown', this.onMousedown, this);
19882 this.picker().on('click', this.onClick, this);
19884 this.picker().addClass('datepicker-dropdown');
19886 this.startViewMode = this.viewMode;
19888 if(this.singleMode){
19889 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19890 v.setVisibilityMode(Roo.Element.DISPLAY);
19894 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19895 v.setStyle('width', '189px');
19899 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19900 if(!this.calendarWeeks){
19905 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19906 v.attr('colspan', function(i, val){
19907 return parseInt(val) + 1;
19912 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19914 this.setStartDate(this.startDate);
19915 this.setEndDate(this.endDate);
19917 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19924 if(this.isInline) {
19929 picker : function()
19931 return this.pickerEl;
19932 // return this.el.select('.datepicker', true).first();
19935 fillDow: function()
19937 var dowCnt = this.weekStart;
19946 if(this.calendarWeeks){
19954 while (dowCnt < this.weekStart + 7) {
19958 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19962 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19965 fillMonths: function()
19968 var months = this.picker().select('>.datepicker-months td', true).first();
19970 months.dom.innerHTML = '';
19976 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19979 months.createChild(month);
19986 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;
19988 if (this.date < this.startDate) {
19989 this.viewDate = new Date(this.startDate);
19990 } else if (this.date > this.endDate) {
19991 this.viewDate = new Date(this.endDate);
19993 this.viewDate = new Date(this.date);
20001 var d = new Date(this.viewDate),
20002 year = d.getUTCFullYear(),
20003 month = d.getUTCMonth(),
20004 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20005 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20006 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20007 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20008 currentDate = this.date && this.date.valueOf(),
20009 today = this.UTCToday();
20011 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20013 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20015 // this.picker.select('>tfoot th.today').
20016 // .text(dates[this.language].today)
20017 // .toggle(this.todayBtn !== false);
20019 this.updateNavArrows();
20022 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20024 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20026 prevMonth.setUTCDate(day);
20028 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20030 var nextMonth = new Date(prevMonth);
20032 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20034 nextMonth = nextMonth.valueOf();
20036 var fillMonths = false;
20038 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20040 while(prevMonth.valueOf() <= nextMonth) {
20043 if (prevMonth.getUTCDay() === this.weekStart) {
20045 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20053 if(this.calendarWeeks){
20054 // ISO 8601: First week contains first thursday.
20055 // ISO also states week starts on Monday, but we can be more abstract here.
20057 // Start of current week: based on weekstart/current date
20058 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20059 // Thursday of this week
20060 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20061 // First Thursday of year, year from thursday
20062 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20063 // Calendar week: ms between thursdays, div ms per day, div 7 days
20064 calWeek = (th - yth) / 864e5 / 7 + 1;
20066 fillMonths.cn.push({
20074 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20076 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20079 if (this.todayHighlight &&
20080 prevMonth.getUTCFullYear() == today.getFullYear() &&
20081 prevMonth.getUTCMonth() == today.getMonth() &&
20082 prevMonth.getUTCDate() == today.getDate()) {
20083 clsName += ' today';
20086 if (currentDate && prevMonth.valueOf() === currentDate) {
20087 clsName += ' active';
20090 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20091 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20092 clsName += ' disabled';
20095 fillMonths.cn.push({
20097 cls: 'day ' + clsName,
20098 html: prevMonth.getDate()
20101 prevMonth.setDate(prevMonth.getDate()+1);
20104 var currentYear = this.date && this.date.getUTCFullYear();
20105 var currentMonth = this.date && this.date.getUTCMonth();
20107 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20109 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20110 v.removeClass('active');
20112 if(currentYear === year && k === currentMonth){
20113 v.addClass('active');
20116 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20117 v.addClass('disabled');
20123 year = parseInt(year/10, 10) * 10;
20125 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20127 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20130 for (var i = -1; i < 11; i++) {
20131 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20133 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20141 showMode: function(dir)
20144 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20147 Roo.each(this.picker().select('>div',true).elements, function(v){
20148 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20151 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20156 if(this.isInline) {
20160 this.picker().removeClass(['bottom', 'top']);
20162 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20164 * place to the top of element!
20168 this.picker().addClass('top');
20169 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20174 this.picker().addClass('bottom');
20176 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20179 parseDate : function(value)
20181 if(!value || value instanceof Date){
20184 var v = Date.parseDate(value, this.format);
20185 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20186 v = Date.parseDate(value, 'Y-m-d');
20188 if(!v && this.altFormats){
20189 if(!this.altFormatsArray){
20190 this.altFormatsArray = this.altFormats.split("|");
20192 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20193 v = Date.parseDate(value, this.altFormatsArray[i]);
20199 formatDate : function(date, fmt)
20201 return (!date || !(date instanceof Date)) ?
20202 date : date.dateFormat(fmt || this.format);
20205 onFocus : function()
20207 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20211 onBlur : function()
20213 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20215 var d = this.inputEl().getValue();
20222 showPopup : function()
20224 this.picker().show();
20228 this.fireEvent('showpopup', this, this.date);
20231 hidePopup : function()
20233 if(this.isInline) {
20236 this.picker().hide();
20237 this.viewMode = this.startViewMode;
20240 this.fireEvent('hidepopup', this, this.date);
20244 onMousedown: function(e)
20246 e.stopPropagation();
20247 e.preventDefault();
20252 Roo.bootstrap.DateField.superclass.keyup.call(this);
20256 setValue: function(v)
20258 if(this.fireEvent('beforeselect', this, v) !== false){
20259 var d = new Date(this.parseDate(v) ).clearTime();
20261 if(isNaN(d.getTime())){
20262 this.date = this.viewDate = '';
20263 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20267 v = this.formatDate(d);
20269 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20271 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20275 this.fireEvent('select', this, this.date);
20279 getValue: function()
20281 return this.formatDate(this.date);
20284 fireKey: function(e)
20286 if (!this.picker().isVisible()){
20287 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20293 var dateChanged = false,
20295 newDate, newViewDate;
20300 e.preventDefault();
20304 if (!this.keyboardNavigation) {
20307 dir = e.keyCode == 37 ? -1 : 1;
20310 newDate = this.moveYear(this.date, dir);
20311 newViewDate = this.moveYear(this.viewDate, dir);
20312 } else if (e.shiftKey){
20313 newDate = this.moveMonth(this.date, dir);
20314 newViewDate = this.moveMonth(this.viewDate, dir);
20316 newDate = new Date(this.date);
20317 newDate.setUTCDate(this.date.getUTCDate() + dir);
20318 newViewDate = new Date(this.viewDate);
20319 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20321 if (this.dateWithinRange(newDate)){
20322 this.date = newDate;
20323 this.viewDate = newViewDate;
20324 this.setValue(this.formatDate(this.date));
20326 e.preventDefault();
20327 dateChanged = true;
20332 if (!this.keyboardNavigation) {
20335 dir = e.keyCode == 38 ? -1 : 1;
20337 newDate = this.moveYear(this.date, dir);
20338 newViewDate = this.moveYear(this.viewDate, dir);
20339 } else if (e.shiftKey){
20340 newDate = this.moveMonth(this.date, dir);
20341 newViewDate = this.moveMonth(this.viewDate, dir);
20343 newDate = new Date(this.date);
20344 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20345 newViewDate = new Date(this.viewDate);
20346 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20348 if (this.dateWithinRange(newDate)){
20349 this.date = newDate;
20350 this.viewDate = newViewDate;
20351 this.setValue(this.formatDate(this.date));
20353 e.preventDefault();
20354 dateChanged = true;
20358 this.setValue(this.formatDate(this.date));
20360 e.preventDefault();
20363 this.setValue(this.formatDate(this.date));
20377 onClick: function(e)
20379 e.stopPropagation();
20380 e.preventDefault();
20382 var target = e.getTarget();
20384 if(target.nodeName.toLowerCase() === 'i'){
20385 target = Roo.get(target).dom.parentNode;
20388 var nodeName = target.nodeName;
20389 var className = target.className;
20390 var html = target.innerHTML;
20391 //Roo.log(nodeName);
20393 switch(nodeName.toLowerCase()) {
20395 switch(className) {
20401 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20402 switch(this.viewMode){
20404 this.viewDate = this.moveMonth(this.viewDate, dir);
20408 this.viewDate = this.moveYear(this.viewDate, dir);
20414 var date = new Date();
20415 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20417 this.setValue(this.formatDate(this.date));
20424 if (className.indexOf('disabled') < 0) {
20425 this.viewDate.setUTCDate(1);
20426 if (className.indexOf('month') > -1) {
20427 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20429 var year = parseInt(html, 10) || 0;
20430 this.viewDate.setUTCFullYear(year);
20434 if(this.singleMode){
20435 this.setValue(this.formatDate(this.viewDate));
20446 //Roo.log(className);
20447 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20448 var day = parseInt(html, 10) || 1;
20449 var year = this.viewDate.getUTCFullYear(),
20450 month = this.viewDate.getUTCMonth();
20452 if (className.indexOf('old') > -1) {
20459 } else if (className.indexOf('new') > -1) {
20467 //Roo.log([year,month,day]);
20468 this.date = this.UTCDate(year, month, day,0,0,0,0);
20469 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20471 //Roo.log(this.formatDate(this.date));
20472 this.setValue(this.formatDate(this.date));
20479 setStartDate: function(startDate)
20481 this.startDate = startDate || -Infinity;
20482 if (this.startDate !== -Infinity) {
20483 this.startDate = this.parseDate(this.startDate);
20486 this.updateNavArrows();
20489 setEndDate: function(endDate)
20491 this.endDate = endDate || Infinity;
20492 if (this.endDate !== Infinity) {
20493 this.endDate = this.parseDate(this.endDate);
20496 this.updateNavArrows();
20499 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20501 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20502 if (typeof(this.daysOfWeekDisabled) !== 'object') {
20503 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20505 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20506 return parseInt(d, 10);
20509 this.updateNavArrows();
20512 updateNavArrows: function()
20514 if(this.singleMode){
20518 var d = new Date(this.viewDate),
20519 year = d.getUTCFullYear(),
20520 month = d.getUTCMonth();
20522 Roo.each(this.picker().select('.prev', true).elements, function(v){
20524 switch (this.viewMode) {
20527 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20533 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20540 Roo.each(this.picker().select('.next', true).elements, function(v){
20542 switch (this.viewMode) {
20545 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20551 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20559 moveMonth: function(date, dir)
20564 var new_date = new Date(date.valueOf()),
20565 day = new_date.getUTCDate(),
20566 month = new_date.getUTCMonth(),
20567 mag = Math.abs(dir),
20569 dir = dir > 0 ? 1 : -1;
20572 // If going back one month, make sure month is not current month
20573 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20575 return new_date.getUTCMonth() == month;
20577 // If going forward one month, make sure month is as expected
20578 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20580 return new_date.getUTCMonth() != new_month;
20582 new_month = month + dir;
20583 new_date.setUTCMonth(new_month);
20584 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20585 if (new_month < 0 || new_month > 11) {
20586 new_month = (new_month + 12) % 12;
20589 // For magnitudes >1, move one month at a time...
20590 for (var i=0; i<mag; i++) {
20591 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20592 new_date = this.moveMonth(new_date, dir);
20594 // ...then reset the day, keeping it in the new month
20595 new_month = new_date.getUTCMonth();
20596 new_date.setUTCDate(day);
20598 return new_month != new_date.getUTCMonth();
20601 // Common date-resetting loop -- if date is beyond end of month, make it
20604 new_date.setUTCDate(--day);
20605 new_date.setUTCMonth(new_month);
20610 moveYear: function(date, dir)
20612 return this.moveMonth(date, dir*12);
20615 dateWithinRange: function(date)
20617 return date >= this.startDate && date <= this.endDate;
20623 this.picker().remove();
20626 validateValue : function(value)
20628 if(this.getVisibilityEl().hasClass('hidden')){
20632 if(value.length < 1) {
20633 if(this.allowBlank){
20639 if(value.length < this.minLength){
20642 if(value.length > this.maxLength){
20646 var vt = Roo.form.VTypes;
20647 if(!vt[this.vtype](value, this)){
20651 if(typeof this.validator == "function"){
20652 var msg = this.validator(value);
20658 if(this.regex && !this.regex.test(value)){
20662 if(typeof(this.parseDate(value)) == 'undefined'){
20666 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20670 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20680 this.date = this.viewDate = '';
20682 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20687 Roo.apply(Roo.bootstrap.DateField, {
20698 html: '<i class="fa fa-arrow-left"/>'
20708 html: '<i class="fa fa-arrow-right"/>'
20750 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20751 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20752 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20753 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20754 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20767 navFnc: 'FullYear',
20772 navFnc: 'FullYear',
20777 Roo.apply(Roo.bootstrap.DateField, {
20781 cls: 'datepicker dropdown-menu roo-dynamic',
20785 cls: 'datepicker-days',
20789 cls: 'table-condensed',
20791 Roo.bootstrap.DateField.head,
20795 Roo.bootstrap.DateField.footer
20802 cls: 'datepicker-months',
20806 cls: 'table-condensed',
20808 Roo.bootstrap.DateField.head,
20809 Roo.bootstrap.DateField.content,
20810 Roo.bootstrap.DateField.footer
20817 cls: 'datepicker-years',
20821 cls: 'table-condensed',
20823 Roo.bootstrap.DateField.head,
20824 Roo.bootstrap.DateField.content,
20825 Roo.bootstrap.DateField.footer
20844 * @class Roo.bootstrap.TimeField
20845 * @extends Roo.bootstrap.Input
20846 * Bootstrap DateField class
20850 * Create a new TimeField
20851 * @param {Object} config The config object
20854 Roo.bootstrap.TimeField = function(config){
20855 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20859 * Fires when this field show.
20860 * @param {Roo.bootstrap.DateField} thisthis
20861 * @param {Mixed} date The date value
20866 * Fires when this field hide.
20867 * @param {Roo.bootstrap.DateField} this
20868 * @param {Mixed} date The date value
20873 * Fires when select a date.
20874 * @param {Roo.bootstrap.DateField} this
20875 * @param {Mixed} date The date value
20881 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20884 * @cfg {String} format
20885 * The default time format string which can be overriden for localization support. The format must be
20886 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20890 onRender: function(ct, position)
20893 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20895 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20897 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20899 this.pop = this.picker().select('>.datepicker-time',true).first();
20900 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20902 this.picker().on('mousedown', this.onMousedown, this);
20903 this.picker().on('click', this.onClick, this);
20905 this.picker().addClass('datepicker-dropdown');
20910 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20911 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20912 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20913 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20914 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20915 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20919 fireKey: function(e){
20920 if (!this.picker().isVisible()){
20921 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20927 e.preventDefault();
20935 this.onTogglePeriod();
20938 this.onIncrementMinutes();
20941 this.onDecrementMinutes();
20950 onClick: function(e) {
20951 e.stopPropagation();
20952 e.preventDefault();
20955 picker : function()
20957 return this.el.select('.datepicker', true).first();
20960 fillTime: function()
20962 var time = this.pop.select('tbody', true).first();
20964 time.dom.innerHTML = '';
20979 cls: 'hours-up glyphicon glyphicon-chevron-up'
20999 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21020 cls: 'timepicker-hour',
21035 cls: 'timepicker-minute',
21050 cls: 'btn btn-primary period',
21072 cls: 'hours-down glyphicon glyphicon-chevron-down'
21092 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21110 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21117 var hours = this.time.getHours();
21118 var minutes = this.time.getMinutes();
21131 hours = hours - 12;
21135 hours = '0' + hours;
21139 minutes = '0' + minutes;
21142 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21143 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21144 this.pop.select('button', true).first().dom.innerHTML = period;
21150 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21152 var cls = ['bottom'];
21154 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21161 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21166 this.picker().addClass(cls.join('-'));
21170 Roo.each(cls, function(c){
21172 _this.picker().setTop(_this.inputEl().getHeight());
21176 _this.picker().setTop(0 - _this.picker().getHeight());
21181 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21185 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21192 onFocus : function()
21194 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21198 onBlur : function()
21200 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21206 this.picker().show();
21211 this.fireEvent('show', this, this.date);
21216 this.picker().hide();
21219 this.fireEvent('hide', this, this.date);
21222 setTime : function()
21225 this.setValue(this.time.format(this.format));
21227 this.fireEvent('select', this, this.date);
21232 onMousedown: function(e){
21233 e.stopPropagation();
21234 e.preventDefault();
21237 onIncrementHours: function()
21239 Roo.log('onIncrementHours');
21240 this.time = this.time.add(Date.HOUR, 1);
21245 onDecrementHours: function()
21247 Roo.log('onDecrementHours');
21248 this.time = this.time.add(Date.HOUR, -1);
21252 onIncrementMinutes: function()
21254 Roo.log('onIncrementMinutes');
21255 this.time = this.time.add(Date.MINUTE, 1);
21259 onDecrementMinutes: function()
21261 Roo.log('onDecrementMinutes');
21262 this.time = this.time.add(Date.MINUTE, -1);
21266 onTogglePeriod: function()
21268 Roo.log('onTogglePeriod');
21269 this.time = this.time.add(Date.HOUR, 12);
21276 Roo.apply(Roo.bootstrap.TimeField, {
21306 cls: 'btn btn-info ok',
21318 Roo.apply(Roo.bootstrap.TimeField, {
21322 cls: 'datepicker dropdown-menu',
21326 cls: 'datepicker-time',
21330 cls: 'table-condensed',
21332 Roo.bootstrap.TimeField.content,
21333 Roo.bootstrap.TimeField.footer
21352 * @class Roo.bootstrap.MonthField
21353 * @extends Roo.bootstrap.Input
21354 * Bootstrap MonthField class
21356 * @cfg {String} language default en
21359 * Create a new MonthField
21360 * @param {Object} config The config object
21363 Roo.bootstrap.MonthField = function(config){
21364 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21369 * Fires when this field show.
21370 * @param {Roo.bootstrap.MonthField} this
21371 * @param {Mixed} date The date value
21376 * Fires when this field hide.
21377 * @param {Roo.bootstrap.MonthField} this
21378 * @param {Mixed} date The date value
21383 * Fires when select a date.
21384 * @param {Roo.bootstrap.MonthField} this
21385 * @param {String} oldvalue The old value
21386 * @param {String} newvalue The new value
21392 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
21394 onRender: function(ct, position)
21397 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21399 this.language = this.language || 'en';
21400 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21401 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21403 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21404 this.isInline = false;
21405 this.isInput = true;
21406 this.component = this.el.select('.add-on', true).first() || false;
21407 this.component = (this.component && this.component.length === 0) ? false : this.component;
21408 this.hasInput = this.component && this.inputEL().length;
21410 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21412 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21414 this.picker().on('mousedown', this.onMousedown, this);
21415 this.picker().on('click', this.onClick, this);
21417 this.picker().addClass('datepicker-dropdown');
21419 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21420 v.setStyle('width', '189px');
21427 if(this.isInline) {
21433 setValue: function(v, suppressEvent)
21435 var o = this.getValue();
21437 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21441 if(suppressEvent !== true){
21442 this.fireEvent('select', this, o, v);
21447 getValue: function()
21452 onClick: function(e)
21454 e.stopPropagation();
21455 e.preventDefault();
21457 var target = e.getTarget();
21459 if(target.nodeName.toLowerCase() === 'i'){
21460 target = Roo.get(target).dom.parentNode;
21463 var nodeName = target.nodeName;
21464 var className = target.className;
21465 var html = target.innerHTML;
21467 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21471 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21473 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21479 picker : function()
21481 return this.pickerEl;
21484 fillMonths: function()
21487 var months = this.picker().select('>.datepicker-months td', true).first();
21489 months.dom.innerHTML = '';
21495 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21498 months.createChild(month);
21507 if(typeof(this.vIndex) == 'undefined' && this.value.length){
21508 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21511 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21512 e.removeClass('active');
21514 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21515 e.addClass('active');
21522 if(this.isInline) {
21526 this.picker().removeClass(['bottom', 'top']);
21528 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21530 * place to the top of element!
21534 this.picker().addClass('top');
21535 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21540 this.picker().addClass('bottom');
21542 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21545 onFocus : function()
21547 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21551 onBlur : function()
21553 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21555 var d = this.inputEl().getValue();
21564 this.picker().show();
21565 this.picker().select('>.datepicker-months', true).first().show();
21569 this.fireEvent('show', this, this.date);
21574 if(this.isInline) {
21577 this.picker().hide();
21578 this.fireEvent('hide', this, this.date);
21582 onMousedown: function(e)
21584 e.stopPropagation();
21585 e.preventDefault();
21590 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21594 fireKey: function(e)
21596 if (!this.picker().isVisible()){
21597 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21608 e.preventDefault();
21612 dir = e.keyCode == 37 ? -1 : 1;
21614 this.vIndex = this.vIndex + dir;
21616 if(this.vIndex < 0){
21620 if(this.vIndex > 11){
21624 if(isNaN(this.vIndex)){
21628 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21634 dir = e.keyCode == 38 ? -1 : 1;
21636 this.vIndex = this.vIndex + dir * 4;
21638 if(this.vIndex < 0){
21642 if(this.vIndex > 11){
21646 if(isNaN(this.vIndex)){
21650 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21655 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21656 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21660 e.preventDefault();
21663 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21664 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21680 this.picker().remove();
21685 Roo.apply(Roo.bootstrap.MonthField, {
21704 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21705 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21710 Roo.apply(Roo.bootstrap.MonthField, {
21714 cls: 'datepicker dropdown-menu roo-dynamic',
21718 cls: 'datepicker-months',
21722 cls: 'table-condensed',
21724 Roo.bootstrap.DateField.content
21744 * @class Roo.bootstrap.CheckBox
21745 * @extends Roo.bootstrap.Input
21746 * Bootstrap CheckBox class
21748 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21749 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21750 * @cfg {String} boxLabel The text that appears beside the checkbox
21751 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21752 * @cfg {Boolean} checked initnal the element
21753 * @cfg {Boolean} inline inline the element (default false)
21754 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21755 * @cfg {String} tooltip label tooltip
21758 * Create a new CheckBox
21759 * @param {Object} config The config object
21762 Roo.bootstrap.CheckBox = function(config){
21763 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21768 * Fires when the element is checked or unchecked.
21769 * @param {Roo.bootstrap.CheckBox} this This input
21770 * @param {Boolean} checked The new checked value
21775 * Fires when the element is click.
21776 * @param {Roo.bootstrap.CheckBox} this This input
21783 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21785 inputType: 'checkbox',
21794 // checkbox success does not make any sense really..
21799 getAutoCreate : function()
21801 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21807 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21810 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21816 type : this.inputType,
21817 value : this.inputValue,
21818 cls : 'roo-' + this.inputType, //'form-box',
21819 placeholder : this.placeholder || ''
21823 if(this.inputType != 'radio'){
21827 cls : 'roo-hidden-value',
21828 value : this.checked ? this.inputValue : this.valueOff
21833 if (this.weight) { // Validity check?
21834 cfg.cls += " " + this.inputType + "-" + this.weight;
21837 if (this.disabled) {
21838 input.disabled=true;
21842 input.checked = this.checked;
21847 input.name = this.name;
21849 if(this.inputType != 'radio'){
21850 hidden.name = this.name;
21851 input.name = '_hidden_' + this.name;
21856 input.cls += ' input-' + this.size;
21861 ['xs','sm','md','lg'].map(function(size){
21862 if (settings[size]) {
21863 cfg.cls += ' col-' + size + '-' + settings[size];
21867 var inputblock = input;
21869 if (this.before || this.after) {
21872 cls : 'input-group',
21877 inputblock.cn.push({
21879 cls : 'input-group-addon',
21884 inputblock.cn.push(input);
21886 if(this.inputType != 'radio'){
21887 inputblock.cn.push(hidden);
21891 inputblock.cn.push({
21893 cls : 'input-group-addon',
21899 var boxLabelCfg = false;
21905 //'for': id, // box label is handled by onclick - so no for...
21907 html: this.boxLabel
21910 boxLabelCfg.tooltip = this.tooltip;
21916 if (align ==='left' && this.fieldLabel.length) {
21917 // Roo.log("left and has label");
21922 cls : 'control-label',
21923 html : this.fieldLabel
21934 cfg.cn[1].cn.push(boxLabelCfg);
21937 if(this.labelWidth > 12){
21938 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21941 if(this.labelWidth < 13 && this.labelmd == 0){
21942 this.labelmd = this.labelWidth;
21945 if(this.labellg > 0){
21946 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21947 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21950 if(this.labelmd > 0){
21951 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21952 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21955 if(this.labelsm > 0){
21956 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21957 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21960 if(this.labelxs > 0){
21961 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21962 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21965 } else if ( this.fieldLabel.length) {
21966 // Roo.log(" label");
21970 tag: this.boxLabel ? 'span' : 'label',
21972 cls: 'control-label box-input-label',
21973 //cls : 'input-group-addon',
21974 html : this.fieldLabel
21981 cfg.cn.push(boxLabelCfg);
21986 // Roo.log(" no label && no align");
21987 cfg.cn = [ inputblock ] ;
21989 cfg.cn.push(boxLabelCfg);
21997 if(this.inputType != 'radio'){
21998 cfg.cn.push(hidden);
22006 * return the real input element.
22008 inputEl: function ()
22010 return this.el.select('input.roo-' + this.inputType,true).first();
22012 hiddenEl: function ()
22014 return this.el.select('input.roo-hidden-value',true).first();
22017 labelEl: function()
22019 return this.el.select('label.control-label',true).first();
22021 /* depricated... */
22025 return this.labelEl();
22028 boxLabelEl: function()
22030 return this.el.select('label.box-label',true).first();
22033 initEvents : function()
22035 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22037 this.inputEl().on('click', this.onClick, this);
22039 if (this.boxLabel) {
22040 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22043 this.startValue = this.getValue();
22046 Roo.bootstrap.CheckBox.register(this);
22050 onClick : function(e)
22052 if(this.fireEvent('click', this, e) !== false){
22053 this.setChecked(!this.checked);
22058 setChecked : function(state,suppressEvent)
22060 this.startValue = this.getValue();
22062 if(this.inputType == 'radio'){
22064 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22065 e.dom.checked = false;
22068 this.inputEl().dom.checked = true;
22070 this.inputEl().dom.value = this.inputValue;
22072 if(suppressEvent !== true){
22073 this.fireEvent('check', this, true);
22081 this.checked = state;
22083 this.inputEl().dom.checked = state;
22086 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22088 if(suppressEvent !== true){
22089 this.fireEvent('check', this, state);
22095 getValue : function()
22097 if(this.inputType == 'radio'){
22098 return this.getGroupValue();
22101 return this.hiddenEl().dom.value;
22105 getGroupValue : function()
22107 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22111 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22114 setValue : function(v,suppressEvent)
22116 if(this.inputType == 'radio'){
22117 this.setGroupValue(v, suppressEvent);
22121 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22126 setGroupValue : function(v, suppressEvent)
22128 this.startValue = this.getValue();
22130 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22131 e.dom.checked = false;
22133 if(e.dom.value == v){
22134 e.dom.checked = true;
22138 if(suppressEvent !== true){
22139 this.fireEvent('check', this, true);
22147 validate : function()
22149 if(this.getVisibilityEl().hasClass('hidden')){
22155 (this.inputType == 'radio' && this.validateRadio()) ||
22156 (this.inputType == 'checkbox' && this.validateCheckbox())
22162 this.markInvalid();
22166 validateRadio : function()
22168 if(this.getVisibilityEl().hasClass('hidden')){
22172 if(this.allowBlank){
22178 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22179 if(!e.dom.checked){
22191 validateCheckbox : function()
22194 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22195 //return (this.getValue() == this.inputValue) ? true : false;
22198 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22206 for(var i in group){
22207 if(group[i].el.isVisible(true)){
22215 for(var i in group){
22220 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22227 * Mark this field as valid
22229 markValid : function()
22233 this.fireEvent('valid', this);
22235 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22238 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22245 if(this.inputType == 'radio'){
22246 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22247 var fg = e.findParent('.form-group', false, true);
22248 if (Roo.bootstrap.version == 3) {
22249 fg.removeClass([_this.invalidClass, _this.validClass]);
22250 fg.addClass(_this.validClass);
22252 fg.removeClass(['is-valid', 'is-invalid']);
22253 fg.addClass('is-valid');
22261 var fg = this.el.findParent('.form-group', false, true);
22262 if (Roo.bootstrap.version == 3) {
22263 fg.removeClass([this.invalidClass, this.validClass]);
22264 fg.addClass(this.validClass);
22266 fg.removeClass(['is-valid', 'is-invalid']);
22267 fg.addClass('is-valid');
22272 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22278 for(var i in group){
22279 var fg = group[i].el.findParent('.form-group', false, true);
22280 if (Roo.bootstrap.version == 3) {
22281 fg.removeClass([this.invalidClass, this.validClass]);
22282 fg.addClass(this.validClass);
22284 fg.removeClass(['is-valid', 'is-invalid']);
22285 fg.addClass('is-valid');
22291 * Mark this field as invalid
22292 * @param {String} msg The validation message
22294 markInvalid : function(msg)
22296 if(this.allowBlank){
22302 this.fireEvent('invalid', this, msg);
22304 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22307 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22311 label.markInvalid();
22314 if(this.inputType == 'radio'){
22316 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22317 var fg = e.findParent('.form-group', false, true);
22318 if (Roo.bootstrap.version == 3) {
22319 fg.removeClass([_this.invalidClass, _this.validClass]);
22320 fg.addClass(_this.invalidClass);
22322 fg.removeClass(['is-invalid', 'is-valid']);
22323 fg.addClass('is-invalid');
22331 var fg = this.el.findParent('.form-group', false, true);
22332 if (Roo.bootstrap.version == 3) {
22333 fg.removeClass([_this.invalidClass, _this.validClass]);
22334 fg.addClass(_this.invalidClass);
22336 fg.removeClass(['is-invalid', 'is-valid']);
22337 fg.addClass('is-invalid');
22342 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22348 for(var i in group){
22349 var fg = group[i].el.findParent('.form-group', false, true);
22350 if (Roo.bootstrap.version == 3) {
22351 fg.removeClass([_this.invalidClass, _this.validClass]);
22352 fg.addClass(_this.invalidClass);
22354 fg.removeClass(['is-invalid', 'is-valid']);
22355 fg.addClass('is-invalid');
22361 clearInvalid : function()
22363 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22365 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22367 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22369 if (label && label.iconEl) {
22370 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22371 label.iconEl.removeClass(['is-invalid', 'is-valid']);
22375 disable : function()
22377 if(this.inputType != 'radio'){
22378 Roo.bootstrap.CheckBox.superclass.disable.call(this);
22385 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22386 _this.getActionEl().addClass(this.disabledClass);
22387 e.dom.disabled = true;
22391 this.disabled = true;
22392 this.fireEvent("disable", this);
22396 enable : function()
22398 if(this.inputType != 'radio'){
22399 Roo.bootstrap.CheckBox.superclass.enable.call(this);
22406 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22407 _this.getActionEl().removeClass(this.disabledClass);
22408 e.dom.disabled = false;
22412 this.disabled = false;
22413 this.fireEvent("enable", this);
22417 setBoxLabel : function(v)
22422 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22428 Roo.apply(Roo.bootstrap.CheckBox, {
22433 * register a CheckBox Group
22434 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22436 register : function(checkbox)
22438 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22439 this.groups[checkbox.groupId] = {};
22442 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22446 this.groups[checkbox.groupId][checkbox.name] = checkbox;
22450 * fetch a CheckBox Group based on the group ID
22451 * @param {string} the group ID
22452 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22454 get: function(groupId) {
22455 if (typeof(this.groups[groupId]) == 'undefined') {
22459 return this.groups[groupId] ;
22472 * @class Roo.bootstrap.Radio
22473 * @extends Roo.bootstrap.Component
22474 * Bootstrap Radio class
22475 * @cfg {String} boxLabel - the label associated
22476 * @cfg {String} value - the value of radio
22479 * Create a new Radio
22480 * @param {Object} config The config object
22482 Roo.bootstrap.Radio = function(config){
22483 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22487 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22493 getAutoCreate : function()
22497 cls : 'form-group radio',
22502 html : this.boxLabel
22510 initEvents : function()
22512 this.parent().register(this);
22514 this.el.on('click', this.onClick, this);
22518 onClick : function(e)
22520 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22521 this.setChecked(true);
22525 setChecked : function(state, suppressEvent)
22527 this.parent().setValue(this.value, suppressEvent);
22531 setBoxLabel : function(v)
22536 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22551 * @class Roo.bootstrap.SecurePass
22552 * @extends Roo.bootstrap.Input
22553 * Bootstrap SecurePass class
22557 * Create a new SecurePass
22558 * @param {Object} config The config object
22561 Roo.bootstrap.SecurePass = function (config) {
22562 // these go here, so the translation tool can replace them..
22564 PwdEmpty: "Please type a password, and then retype it to confirm.",
22565 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22566 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22567 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22568 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22569 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22570 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22571 TooWeak: "Your password is Too Weak."
22573 this.meterLabel = "Password strength:";
22574 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22575 this.meterClass = [
22576 "roo-password-meter-tooweak",
22577 "roo-password-meter-weak",
22578 "roo-password-meter-medium",
22579 "roo-password-meter-strong",
22580 "roo-password-meter-grey"
22585 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22588 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22590 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22592 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22593 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22594 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22595 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22596 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22597 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22598 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22608 * @cfg {String/Object} Label for the strength meter (defaults to
22609 * 'Password strength:')
22614 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22615 * ['Weak', 'Medium', 'Strong'])
22618 pwdStrengths: false,
22631 initEvents: function ()
22633 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22635 if (this.el.is('input[type=password]') && Roo.isSafari) {
22636 this.el.on('keydown', this.SafariOnKeyDown, this);
22639 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22642 onRender: function (ct, position)
22644 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22645 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22646 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22648 this.trigger.createChild({
22653 cls: 'roo-password-meter-grey col-xs-12',
22656 //width: this.meterWidth + 'px'
22660 cls: 'roo-password-meter-text'
22666 if (this.hideTrigger) {
22667 this.trigger.setDisplayed(false);
22669 this.setSize(this.width || '', this.height || '');
22672 onDestroy: function ()
22674 if (this.trigger) {
22675 this.trigger.removeAllListeners();
22676 this.trigger.remove();
22679 this.wrap.remove();
22681 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22684 checkStrength: function ()
22686 var pwd = this.inputEl().getValue();
22687 if (pwd == this._lastPwd) {
22692 if (this.ClientSideStrongPassword(pwd)) {
22694 } else if (this.ClientSideMediumPassword(pwd)) {
22696 } else if (this.ClientSideWeakPassword(pwd)) {
22702 Roo.log('strength1: ' + strength);
22704 //var pm = this.trigger.child('div/div/div').dom;
22705 var pm = this.trigger.child('div/div');
22706 pm.removeClass(this.meterClass);
22707 pm.addClass(this.meterClass[strength]);
22710 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22712 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22714 this._lastPwd = pwd;
22718 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22720 this._lastPwd = '';
22722 var pm = this.trigger.child('div/div');
22723 pm.removeClass(this.meterClass);
22724 pm.addClass('roo-password-meter-grey');
22727 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22730 this.inputEl().dom.type='password';
22733 validateValue: function (value)
22736 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22739 if (value.length == 0) {
22740 if (this.allowBlank) {
22741 this.clearInvalid();
22745 this.markInvalid(this.errors.PwdEmpty);
22746 this.errorMsg = this.errors.PwdEmpty;
22754 if ('[\x21-\x7e]*'.match(value)) {
22755 this.markInvalid(this.errors.PwdBadChar);
22756 this.errorMsg = this.errors.PwdBadChar;
22759 if (value.length < 6) {
22760 this.markInvalid(this.errors.PwdShort);
22761 this.errorMsg = this.errors.PwdShort;
22764 if (value.length > 16) {
22765 this.markInvalid(this.errors.PwdLong);
22766 this.errorMsg = this.errors.PwdLong;
22770 if (this.ClientSideStrongPassword(value)) {
22772 } else if (this.ClientSideMediumPassword(value)) {
22774 } else if (this.ClientSideWeakPassword(value)) {
22781 if (strength < 2) {
22782 //this.markInvalid(this.errors.TooWeak);
22783 this.errorMsg = this.errors.TooWeak;
22788 console.log('strength2: ' + strength);
22790 //var pm = this.trigger.child('div/div/div').dom;
22792 var pm = this.trigger.child('div/div');
22793 pm.removeClass(this.meterClass);
22794 pm.addClass(this.meterClass[strength]);
22796 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22798 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22800 this.errorMsg = '';
22804 CharacterSetChecks: function (type)
22807 this.fResult = false;
22810 isctype: function (character, type)
22813 case this.kCapitalLetter:
22814 if (character >= 'A' && character <= 'Z') {
22819 case this.kSmallLetter:
22820 if (character >= 'a' && character <= 'z') {
22826 if (character >= '0' && character <= '9') {
22831 case this.kPunctuation:
22832 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22843 IsLongEnough: function (pwd, size)
22845 return !(pwd == null || isNaN(size) || pwd.length < size);
22848 SpansEnoughCharacterSets: function (word, nb)
22850 if (!this.IsLongEnough(word, nb))
22855 var characterSetChecks = new Array(
22856 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22857 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22860 for (var index = 0; index < word.length; ++index) {
22861 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22862 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22863 characterSetChecks[nCharSet].fResult = true;
22870 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22871 if (characterSetChecks[nCharSet].fResult) {
22876 if (nCharSets < nb) {
22882 ClientSideStrongPassword: function (pwd)
22884 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22887 ClientSideMediumPassword: function (pwd)
22889 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22892 ClientSideWeakPassword: function (pwd)
22894 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22897 })//<script type="text/javascript">
22900 * Based Ext JS Library 1.1.1
22901 * Copyright(c) 2006-2007, Ext JS, LLC.
22907 * @class Roo.HtmlEditorCore
22908 * @extends Roo.Component
22909 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22911 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22914 Roo.HtmlEditorCore = function(config){
22917 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22922 * @event initialize
22923 * Fires when the editor is fully initialized (including the iframe)
22924 * @param {Roo.HtmlEditorCore} this
22929 * Fires when the editor is first receives the focus. Any insertion must wait
22930 * until after this event.
22931 * @param {Roo.HtmlEditorCore} this
22935 * @event beforesync
22936 * Fires before the textarea is updated with content from the editor iframe. Return false
22937 * to cancel the sync.
22938 * @param {Roo.HtmlEditorCore} this
22939 * @param {String} html
22943 * @event beforepush
22944 * Fires before the iframe editor is updated with content from the textarea. Return false
22945 * to cancel the push.
22946 * @param {Roo.HtmlEditorCore} this
22947 * @param {String} html
22952 * Fires when the textarea is updated with content from the editor iframe.
22953 * @param {Roo.HtmlEditorCore} this
22954 * @param {String} html
22959 * Fires when the iframe editor is updated with content from the textarea.
22960 * @param {Roo.HtmlEditorCore} this
22961 * @param {String} html
22966 * @event editorevent
22967 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22968 * @param {Roo.HtmlEditorCore} this
22974 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22976 // defaults : white / black...
22977 this.applyBlacklists();
22984 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22988 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22994 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22999 * @cfg {Number} height (in pixels)
23003 * @cfg {Number} width (in pixels)
23008 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23011 stylesheets: false,
23016 // private properties
23017 validationEvent : false,
23019 initialized : false,
23021 sourceEditMode : false,
23022 onFocus : Roo.emptyFn,
23024 hideMode:'offsets',
23028 // blacklist + whitelisted elements..
23035 * Protected method that will not generally be called directly. It
23036 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23037 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23039 getDocMarkup : function(){
23043 // inherit styels from page...??
23044 if (this.stylesheets === false) {
23046 Roo.get(document.head).select('style').each(function(node) {
23047 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23050 Roo.get(document.head).select('link').each(function(node) {
23051 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23054 } else if (!this.stylesheets.length) {
23056 st = '<style type="text/css">' +
23057 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23060 st = '<style type="text/css">' +
23065 st += '<style type="text/css">' +
23066 'IMG { cursor: pointer } ' +
23069 var cls = 'roo-htmleditor-body';
23071 if(this.bodyCls.length){
23072 cls += ' ' + this.bodyCls;
23075 return '<html><head>' + st +
23076 //<style type="text/css">' +
23077 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23079 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23083 onRender : function(ct, position)
23086 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23087 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23090 this.el.dom.style.border = '0 none';
23091 this.el.dom.setAttribute('tabIndex', -1);
23092 this.el.addClass('x-hidden hide');
23096 if(Roo.isIE){ // fix IE 1px bogus margin
23097 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23101 this.frameId = Roo.id();
23105 var iframe = this.owner.wrap.createChild({
23107 cls: 'form-control', // bootstrap..
23109 name: this.frameId,
23110 frameBorder : 'no',
23111 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23116 this.iframe = iframe.dom;
23118 this.assignDocWin();
23120 this.doc.designMode = 'on';
23123 this.doc.write(this.getDocMarkup());
23127 var task = { // must defer to wait for browser to be ready
23129 //console.log("run task?" + this.doc.readyState);
23130 this.assignDocWin();
23131 if(this.doc.body || this.doc.readyState == 'complete'){
23133 this.doc.designMode="on";
23137 Roo.TaskMgr.stop(task);
23138 this.initEditor.defer(10, this);
23145 Roo.TaskMgr.start(task);
23150 onResize : function(w, h)
23152 Roo.log('resize: ' +w + ',' + h );
23153 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23157 if(typeof w == 'number'){
23159 this.iframe.style.width = w + 'px';
23161 if(typeof h == 'number'){
23163 this.iframe.style.height = h + 'px';
23165 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23172 * Toggles the editor between standard and source edit mode.
23173 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23175 toggleSourceEdit : function(sourceEditMode){
23177 this.sourceEditMode = sourceEditMode === true;
23179 if(this.sourceEditMode){
23181 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23184 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23185 //this.iframe.className = '';
23188 //this.setSize(this.owner.wrap.getSize());
23189 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23196 * Protected method that will not generally be called directly. If you need/want
23197 * custom HTML cleanup, this is the method you should override.
23198 * @param {String} html The HTML to be cleaned
23199 * return {String} The cleaned HTML
23201 cleanHtml : function(html){
23202 html = String(html);
23203 if(html.length > 5){
23204 if(Roo.isSafari){ // strip safari nonsense
23205 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23208 if(html == ' '){
23215 * HTML Editor -> Textarea
23216 * Protected method that will not generally be called directly. Syncs the contents
23217 * of the editor iframe with the textarea.
23219 syncValue : function(){
23220 if(this.initialized){
23221 var bd = (this.doc.body || this.doc.documentElement);
23222 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23223 var html = bd.innerHTML;
23225 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23226 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23228 html = '<div style="'+m[0]+'">' + html + '</div>';
23231 html = this.cleanHtml(html);
23232 // fix up the special chars.. normaly like back quotes in word...
23233 // however we do not want to do this with chinese..
23234 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23236 var cc = match.charCodeAt();
23238 // Get the character value, handling surrogate pairs
23239 if (match.length == 2) {
23240 // It's a surrogate pair, calculate the Unicode code point
23241 var high = match.charCodeAt(0) - 0xD800;
23242 var low = match.charCodeAt(1) - 0xDC00;
23243 cc = (high * 0x400) + low + 0x10000;
23245 (cc >= 0x4E00 && cc < 0xA000 ) ||
23246 (cc >= 0x3400 && cc < 0x4E00 ) ||
23247 (cc >= 0xf900 && cc < 0xfb00 )
23252 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23253 return "&#" + cc + ";";
23260 if(this.owner.fireEvent('beforesync', this, html) !== false){
23261 this.el.dom.value = html;
23262 this.owner.fireEvent('sync', this, html);
23268 * Protected method that will not generally be called directly. Pushes the value of the textarea
23269 * into the iframe editor.
23271 pushValue : function(){
23272 if(this.initialized){
23273 var v = this.el.dom.value.trim();
23275 // if(v.length < 1){
23279 if(this.owner.fireEvent('beforepush', this, v) !== false){
23280 var d = (this.doc.body || this.doc.documentElement);
23282 this.cleanUpPaste();
23283 this.el.dom.value = d.innerHTML;
23284 this.owner.fireEvent('push', this, v);
23290 deferFocus : function(){
23291 this.focus.defer(10, this);
23295 focus : function(){
23296 if(this.win && !this.sourceEditMode){
23303 assignDocWin: function()
23305 var iframe = this.iframe;
23308 this.doc = iframe.contentWindow.document;
23309 this.win = iframe.contentWindow;
23311 // if (!Roo.get(this.frameId)) {
23314 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23315 // this.win = Roo.get(this.frameId).dom.contentWindow;
23317 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23321 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23322 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23327 initEditor : function(){
23328 //console.log("INIT EDITOR");
23329 this.assignDocWin();
23333 this.doc.designMode="on";
23335 this.doc.write(this.getDocMarkup());
23338 var dbody = (this.doc.body || this.doc.documentElement);
23339 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23340 // this copies styles from the containing element into thsi one..
23341 // not sure why we need all of this..
23342 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23344 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23345 //ss['background-attachment'] = 'fixed'; // w3c
23346 dbody.bgProperties = 'fixed'; // ie
23347 //Roo.DomHelper.applyStyles(dbody, ss);
23348 Roo.EventManager.on(this.doc, {
23349 //'mousedown': this.onEditorEvent,
23350 'mouseup': this.onEditorEvent,
23351 'dblclick': this.onEditorEvent,
23352 'click': this.onEditorEvent,
23353 'keyup': this.onEditorEvent,
23358 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23360 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23361 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23363 this.initialized = true;
23365 this.owner.fireEvent('initialize', this);
23370 onDestroy : function(){
23376 //for (var i =0; i < this.toolbars.length;i++) {
23377 // // fixme - ask toolbars for heights?
23378 // this.toolbars[i].onDestroy();
23381 //this.wrap.dom.innerHTML = '';
23382 //this.wrap.remove();
23387 onFirstFocus : function(){
23389 this.assignDocWin();
23392 this.activated = true;
23395 if(Roo.isGecko){ // prevent silly gecko errors
23397 var s = this.win.getSelection();
23398 if(!s.focusNode || s.focusNode.nodeType != 3){
23399 var r = s.getRangeAt(0);
23400 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23405 this.execCmd('useCSS', true);
23406 this.execCmd('styleWithCSS', false);
23409 this.owner.fireEvent('activate', this);
23413 adjustFont: function(btn){
23414 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23415 //if(Roo.isSafari){ // safari
23418 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23419 if(Roo.isSafari){ // safari
23420 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23421 v = (v < 10) ? 10 : v;
23422 v = (v > 48) ? 48 : v;
23423 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23428 v = Math.max(1, v+adjust);
23430 this.execCmd('FontSize', v );
23433 onEditorEvent : function(e)
23435 this.owner.fireEvent('editorevent', this, e);
23436 // this.updateToolbar();
23437 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23440 insertTag : function(tg)
23442 // could be a bit smarter... -> wrap the current selected tRoo..
23443 if (tg.toLowerCase() == 'span' ||
23444 tg.toLowerCase() == 'code' ||
23445 tg.toLowerCase() == 'sup' ||
23446 tg.toLowerCase() == 'sub'
23449 range = this.createRange(this.getSelection());
23450 var wrappingNode = this.doc.createElement(tg.toLowerCase());
23451 wrappingNode.appendChild(range.extractContents());
23452 range.insertNode(wrappingNode);
23459 this.execCmd("formatblock", tg);
23463 insertText : function(txt)
23467 var range = this.createRange();
23468 range.deleteContents();
23469 //alert(Sender.getAttribute('label'));
23471 range.insertNode(this.doc.createTextNode(txt));
23477 * Executes a Midas editor command on the editor document and performs necessary focus and
23478 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23479 * @param {String} cmd The Midas command
23480 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23482 relayCmd : function(cmd, value){
23484 this.execCmd(cmd, value);
23485 this.owner.fireEvent('editorevent', this);
23486 //this.updateToolbar();
23487 this.owner.deferFocus();
23491 * Executes a Midas editor command directly on the editor document.
23492 * For visual commands, you should use {@link #relayCmd} instead.
23493 * <b>This should only be called after the editor is initialized.</b>
23494 * @param {String} cmd The Midas command
23495 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23497 execCmd : function(cmd, value){
23498 this.doc.execCommand(cmd, false, value === undefined ? null : value);
23505 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23507 * @param {String} text | dom node..
23509 insertAtCursor : function(text)
23512 if(!this.activated){
23518 var r = this.doc.selection.createRange();
23529 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23533 // from jquery ui (MIT licenced)
23535 var win = this.win;
23537 if (win.getSelection && win.getSelection().getRangeAt) {
23538 range = win.getSelection().getRangeAt(0);
23539 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23540 range.insertNode(node);
23541 } else if (win.document.selection && win.document.selection.createRange) {
23542 // no firefox support
23543 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23544 win.document.selection.createRange().pasteHTML(txt);
23546 // no firefox support
23547 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23548 this.execCmd('InsertHTML', txt);
23557 mozKeyPress : function(e){
23559 var c = e.getCharCode(), cmd;
23562 c = String.fromCharCode(c).toLowerCase();
23576 this.cleanUpPaste.defer(100, this);
23584 e.preventDefault();
23592 fixKeys : function(){ // load time branching for fastest keydown performance
23594 return function(e){
23595 var k = e.getKey(), r;
23598 r = this.doc.selection.createRange();
23601 r.pasteHTML('    ');
23608 r = this.doc.selection.createRange();
23610 var target = r.parentElement();
23611 if(!target || target.tagName.toLowerCase() != 'li'){
23613 r.pasteHTML('<br />');
23619 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23620 this.cleanUpPaste.defer(100, this);
23626 }else if(Roo.isOpera){
23627 return function(e){
23628 var k = e.getKey();
23632 this.execCmd('InsertHTML','    ');
23635 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23636 this.cleanUpPaste.defer(100, this);
23641 }else if(Roo.isSafari){
23642 return function(e){
23643 var k = e.getKey();
23647 this.execCmd('InsertText','\t');
23651 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23652 this.cleanUpPaste.defer(100, this);
23660 getAllAncestors: function()
23662 var p = this.getSelectedNode();
23665 a.push(p); // push blank onto stack..
23666 p = this.getParentElement();
23670 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23674 a.push(this.doc.body);
23678 lastSelNode : false,
23681 getSelection : function()
23683 this.assignDocWin();
23684 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23687 getSelectedNode: function()
23689 // this may only work on Gecko!!!
23691 // should we cache this!!!!
23696 var range = this.createRange(this.getSelection()).cloneRange();
23699 var parent = range.parentElement();
23701 var testRange = range.duplicate();
23702 testRange.moveToElementText(parent);
23703 if (testRange.inRange(range)) {
23706 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23709 parent = parent.parentElement;
23714 // is ancestor a text element.
23715 var ac = range.commonAncestorContainer;
23716 if (ac.nodeType == 3) {
23717 ac = ac.parentNode;
23720 var ar = ac.childNodes;
23723 var other_nodes = [];
23724 var has_other_nodes = false;
23725 for (var i=0;i<ar.length;i++) {
23726 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23729 // fullly contained node.
23731 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23736 // probably selected..
23737 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23738 other_nodes.push(ar[i]);
23742 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23747 has_other_nodes = true;
23749 if (!nodes.length && other_nodes.length) {
23750 nodes= other_nodes;
23752 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23758 createRange: function(sel)
23760 // this has strange effects when using with
23761 // top toolbar - not sure if it's a great idea.
23762 //this.editor.contentWindow.focus();
23763 if (typeof sel != "undefined") {
23765 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23767 return this.doc.createRange();
23770 return this.doc.createRange();
23773 getParentElement: function()
23776 this.assignDocWin();
23777 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23779 var range = this.createRange(sel);
23782 var p = range.commonAncestorContainer;
23783 while (p.nodeType == 3) { // text node
23794 * Range intersection.. the hard stuff...
23798 * [ -- selected range --- ]
23802 * if end is before start or hits it. fail.
23803 * if start is after end or hits it fail.
23805 * if either hits (but other is outside. - then it's not
23811 // @see http://www.thismuchiknow.co.uk/?p=64.
23812 rangeIntersectsNode : function(range, node)
23814 var nodeRange = node.ownerDocument.createRange();
23816 nodeRange.selectNode(node);
23818 nodeRange.selectNodeContents(node);
23821 var rangeStartRange = range.cloneRange();
23822 rangeStartRange.collapse(true);
23824 var rangeEndRange = range.cloneRange();
23825 rangeEndRange.collapse(false);
23827 var nodeStartRange = nodeRange.cloneRange();
23828 nodeStartRange.collapse(true);
23830 var nodeEndRange = nodeRange.cloneRange();
23831 nodeEndRange.collapse(false);
23833 return rangeStartRange.compareBoundaryPoints(
23834 Range.START_TO_START, nodeEndRange) == -1 &&
23835 rangeEndRange.compareBoundaryPoints(
23836 Range.START_TO_START, nodeStartRange) == 1;
23840 rangeCompareNode : function(range, node)
23842 var nodeRange = node.ownerDocument.createRange();
23844 nodeRange.selectNode(node);
23846 nodeRange.selectNodeContents(node);
23850 range.collapse(true);
23852 nodeRange.collapse(true);
23854 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23855 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23857 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23859 var nodeIsBefore = ss == 1;
23860 var nodeIsAfter = ee == -1;
23862 if (nodeIsBefore && nodeIsAfter) {
23865 if (!nodeIsBefore && nodeIsAfter) {
23866 return 1; //right trailed.
23869 if (nodeIsBefore && !nodeIsAfter) {
23870 return 2; // left trailed.
23876 // private? - in a new class?
23877 cleanUpPaste : function()
23879 // cleans up the whole document..
23880 Roo.log('cleanuppaste');
23882 this.cleanUpChildren(this.doc.body);
23883 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23884 if (clean != this.doc.body.innerHTML) {
23885 this.doc.body.innerHTML = clean;
23890 cleanWordChars : function(input) {// change the chars to hex code
23891 var he = Roo.HtmlEditorCore;
23893 var output = input;
23894 Roo.each(he.swapCodes, function(sw) {
23895 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23897 output = output.replace(swapper, sw[1]);
23904 cleanUpChildren : function (n)
23906 if (!n.childNodes.length) {
23909 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23910 this.cleanUpChild(n.childNodes[i]);
23917 cleanUpChild : function (node)
23920 //console.log(node);
23921 if (node.nodeName == "#text") {
23922 // clean up silly Windows -- stuff?
23925 if (node.nodeName == "#comment") {
23926 node.parentNode.removeChild(node);
23927 // clean up silly Windows -- stuff?
23930 var lcname = node.tagName.toLowerCase();
23931 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23932 // whitelist of tags..
23934 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23936 node.parentNode.removeChild(node);
23941 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23943 // spans with no attributes - just remove them..
23944 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23945 remove_keep_children = true;
23948 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23949 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23951 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23952 // remove_keep_children = true;
23955 if (remove_keep_children) {
23956 this.cleanUpChildren(node);
23957 // inserts everything just before this node...
23958 while (node.childNodes.length) {
23959 var cn = node.childNodes[0];
23960 node.removeChild(cn);
23961 node.parentNode.insertBefore(cn, node);
23963 node.parentNode.removeChild(node);
23967 if (!node.attributes || !node.attributes.length) {
23972 this.cleanUpChildren(node);
23976 function cleanAttr(n,v)
23979 if (v.match(/^\./) || v.match(/^\//)) {
23982 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23985 if (v.match(/^#/)) {
23988 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23989 node.removeAttribute(n);
23993 var cwhite = this.cwhite;
23994 var cblack = this.cblack;
23996 function cleanStyle(n,v)
23998 if (v.match(/expression/)) { //XSS?? should we even bother..
23999 node.removeAttribute(n);
24003 var parts = v.split(/;/);
24006 Roo.each(parts, function(p) {
24007 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24011 var l = p.split(':').shift().replace(/\s+/g,'');
24012 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24014 if ( cwhite.length && cblack.indexOf(l) > -1) {
24015 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24016 //node.removeAttribute(n);
24020 // only allow 'c whitelisted system attributes'
24021 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24022 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24023 //node.removeAttribute(n);
24033 if (clean.length) {
24034 node.setAttribute(n, clean.join(';'));
24036 node.removeAttribute(n);
24042 for (var i = node.attributes.length-1; i > -1 ; i--) {
24043 var a = node.attributes[i];
24046 if (a.name.toLowerCase().substr(0,2)=='on') {
24047 node.removeAttribute(a.name);
24050 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24051 node.removeAttribute(a.name);
24054 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24055 cleanAttr(a.name,a.value); // fixme..
24058 if (a.name == 'style') {
24059 cleanStyle(a.name,a.value);
24062 /// clean up MS crap..
24063 // tecnically this should be a list of valid class'es..
24066 if (a.name == 'class') {
24067 if (a.value.match(/^Mso/)) {
24068 node.removeAttribute('class');
24071 if (a.value.match(/^body$/)) {
24072 node.removeAttribute('class');
24083 this.cleanUpChildren(node);
24089 * Clean up MS wordisms...
24091 cleanWord : function(node)
24094 this.cleanWord(this.doc.body);
24099 node.nodeName == 'SPAN' &&
24100 !node.hasAttributes() &&
24101 node.childNodes.length == 1 &&
24102 node.firstChild.nodeName == "#text"
24104 var textNode = node.firstChild;
24105 node.removeChild(textNode);
24106 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24107 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24109 node.parentNode.insertBefore(textNode, node);
24110 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24111 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24113 node.parentNode.removeChild(node);
24116 if (node.nodeName == "#text") {
24117 // clean up silly Windows -- stuff?
24120 if (node.nodeName == "#comment") {
24121 node.parentNode.removeChild(node);
24122 // clean up silly Windows -- stuff?
24126 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24127 node.parentNode.removeChild(node);
24130 //Roo.log(node.tagName);
24131 // remove - but keep children..
24132 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24133 //Roo.log('-- removed');
24134 while (node.childNodes.length) {
24135 var cn = node.childNodes[0];
24136 node.removeChild(cn);
24137 node.parentNode.insertBefore(cn, node);
24138 // move node to parent - and clean it..
24139 this.cleanWord(cn);
24141 node.parentNode.removeChild(node);
24142 /// no need to iterate chidlren = it's got none..
24143 //this.iterateChildren(node, this.cleanWord);
24147 if (node.className.length) {
24149 var cn = node.className.split(/\W+/);
24151 Roo.each(cn, function(cls) {
24152 if (cls.match(/Mso[a-zA-Z]+/)) {
24157 node.className = cna.length ? cna.join(' ') : '';
24159 node.removeAttribute("class");
24163 if (node.hasAttribute("lang")) {
24164 node.removeAttribute("lang");
24167 if (node.hasAttribute("style")) {
24169 var styles = node.getAttribute("style").split(";");
24171 Roo.each(styles, function(s) {
24172 if (!s.match(/:/)) {
24175 var kv = s.split(":");
24176 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24179 // what ever is left... we allow.
24182 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24183 if (!nstyle.length) {
24184 node.removeAttribute('style');
24187 this.iterateChildren(node, this.cleanWord);
24193 * iterateChildren of a Node, calling fn each time, using this as the scole..
24194 * @param {DomNode} node node to iterate children of.
24195 * @param {Function} fn method of this class to call on each item.
24197 iterateChildren : function(node, fn)
24199 if (!node.childNodes.length) {
24202 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24203 fn.call(this, node.childNodes[i])
24209 * cleanTableWidths.
24211 * Quite often pasting from word etc.. results in tables with column and widths.
24212 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24215 cleanTableWidths : function(node)
24220 this.cleanTableWidths(this.doc.body);
24225 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24228 Roo.log(node.tagName);
24229 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24230 this.iterateChildren(node, this.cleanTableWidths);
24233 if (node.hasAttribute('width')) {
24234 node.removeAttribute('width');
24238 if (node.hasAttribute("style")) {
24241 var styles = node.getAttribute("style").split(";");
24243 Roo.each(styles, function(s) {
24244 if (!s.match(/:/)) {
24247 var kv = s.split(":");
24248 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24251 // what ever is left... we allow.
24254 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24255 if (!nstyle.length) {
24256 node.removeAttribute('style');
24260 this.iterateChildren(node, this.cleanTableWidths);
24268 domToHTML : function(currentElement, depth, nopadtext) {
24270 depth = depth || 0;
24271 nopadtext = nopadtext || false;
24273 if (!currentElement) {
24274 return this.domToHTML(this.doc.body);
24277 //Roo.log(currentElement);
24279 var allText = false;
24280 var nodeName = currentElement.nodeName;
24281 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24283 if (nodeName == '#text') {
24285 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24290 if (nodeName != 'BODY') {
24293 // Prints the node tagName, such as <A>, <IMG>, etc
24296 for(i = 0; i < currentElement.attributes.length;i++) {
24298 var aname = currentElement.attributes.item(i).name;
24299 if (!currentElement.attributes.item(i).value.length) {
24302 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24305 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24314 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24317 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24322 // Traverse the tree
24324 var currentElementChild = currentElement.childNodes.item(i);
24325 var allText = true;
24326 var innerHTML = '';
24328 while (currentElementChild) {
24329 // Formatting code (indent the tree so it looks nice on the screen)
24330 var nopad = nopadtext;
24331 if (lastnode == 'SPAN') {
24335 if (currentElementChild.nodeName == '#text') {
24336 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24337 toadd = nopadtext ? toadd : toadd.trim();
24338 if (!nopad && toadd.length > 80) {
24339 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24341 innerHTML += toadd;
24344 currentElementChild = currentElement.childNodes.item(i);
24350 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
24352 // Recursively traverse the tree structure of the child node
24353 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
24354 lastnode = currentElementChild.nodeName;
24356 currentElementChild=currentElement.childNodes.item(i);
24362 // The remaining code is mostly for formatting the tree
24363 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
24368 ret+= "</"+tagName+">";
24374 applyBlacklists : function()
24376 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
24377 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
24381 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24382 if (b.indexOf(tag) > -1) {
24385 this.white.push(tag);
24389 Roo.each(w, function(tag) {
24390 if (b.indexOf(tag) > -1) {
24393 if (this.white.indexOf(tag) > -1) {
24396 this.white.push(tag);
24401 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24402 if (w.indexOf(tag) > -1) {
24405 this.black.push(tag);
24409 Roo.each(b, function(tag) {
24410 if (w.indexOf(tag) > -1) {
24413 if (this.black.indexOf(tag) > -1) {
24416 this.black.push(tag);
24421 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
24422 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
24426 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24427 if (b.indexOf(tag) > -1) {
24430 this.cwhite.push(tag);
24434 Roo.each(w, function(tag) {
24435 if (b.indexOf(tag) > -1) {
24438 if (this.cwhite.indexOf(tag) > -1) {
24441 this.cwhite.push(tag);
24446 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24447 if (w.indexOf(tag) > -1) {
24450 this.cblack.push(tag);
24454 Roo.each(b, function(tag) {
24455 if (w.indexOf(tag) > -1) {
24458 if (this.cblack.indexOf(tag) > -1) {
24461 this.cblack.push(tag);
24466 setStylesheets : function(stylesheets)
24468 if(typeof(stylesheets) == 'string'){
24469 Roo.get(this.iframe.contentDocument.head).createChild({
24471 rel : 'stylesheet',
24480 Roo.each(stylesheets, function(s) {
24485 Roo.get(_this.iframe.contentDocument.head).createChild({
24487 rel : 'stylesheet',
24496 removeStylesheets : function()
24500 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24505 setStyle : function(style)
24507 Roo.get(this.iframe.contentDocument.head).createChild({
24516 // hide stuff that is not compatible
24530 * @event specialkey
24534 * @cfg {String} fieldClass @hide
24537 * @cfg {String} focusClass @hide
24540 * @cfg {String} autoCreate @hide
24543 * @cfg {String} inputType @hide
24546 * @cfg {String} invalidClass @hide
24549 * @cfg {String} invalidText @hide
24552 * @cfg {String} msgFx @hide
24555 * @cfg {String} validateOnBlur @hide
24559 Roo.HtmlEditorCore.white = [
24560 'area', 'br', 'img', 'input', 'hr', 'wbr',
24562 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24563 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24564 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24565 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24566 'table', 'ul', 'xmp',
24568 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24571 'dir', 'menu', 'ol', 'ul', 'dl',
24577 Roo.HtmlEditorCore.black = [
24578 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24580 'base', 'basefont', 'bgsound', 'blink', 'body',
24581 'frame', 'frameset', 'head', 'html', 'ilayer',
24582 'iframe', 'layer', 'link', 'meta', 'object',
24583 'script', 'style' ,'title', 'xml' // clean later..
24585 Roo.HtmlEditorCore.clean = [
24586 'script', 'style', 'title', 'xml'
24588 Roo.HtmlEditorCore.remove = [
24593 Roo.HtmlEditorCore.ablack = [
24597 Roo.HtmlEditorCore.aclean = [
24598 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24602 Roo.HtmlEditorCore.pwhite= [
24603 'http', 'https', 'mailto'
24606 // white listed style attributes.
24607 Roo.HtmlEditorCore.cwhite= [
24608 // 'text-align', /// default is to allow most things..
24614 // black listed style attributes.
24615 Roo.HtmlEditorCore.cblack= [
24616 // 'font-size' -- this can be set by the project
24620 Roo.HtmlEditorCore.swapCodes =[
24639 * @class Roo.bootstrap.HtmlEditor
24640 * @extends Roo.bootstrap.TextArea
24641 * Bootstrap HtmlEditor class
24644 * Create a new HtmlEditor
24645 * @param {Object} config The config object
24648 Roo.bootstrap.HtmlEditor = function(config){
24649 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24650 if (!this.toolbars) {
24651 this.toolbars = [];
24654 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24657 * @event initialize
24658 * Fires when the editor is fully initialized (including the iframe)
24659 * @param {HtmlEditor} this
24664 * Fires when the editor is first receives the focus. Any insertion must wait
24665 * until after this event.
24666 * @param {HtmlEditor} this
24670 * @event beforesync
24671 * Fires before the textarea is updated with content from the editor iframe. Return false
24672 * to cancel the sync.
24673 * @param {HtmlEditor} this
24674 * @param {String} html
24678 * @event beforepush
24679 * Fires before the iframe editor is updated with content from the textarea. Return false
24680 * to cancel the push.
24681 * @param {HtmlEditor} this
24682 * @param {String} html
24687 * Fires when the textarea is updated with content from the editor iframe.
24688 * @param {HtmlEditor} this
24689 * @param {String} html
24694 * Fires when the iframe editor is updated with content from the textarea.
24695 * @param {HtmlEditor} this
24696 * @param {String} html
24700 * @event editmodechange
24701 * Fires when the editor switches edit modes
24702 * @param {HtmlEditor} this
24703 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24705 editmodechange: true,
24707 * @event editorevent
24708 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24709 * @param {HtmlEditor} this
24713 * @event firstfocus
24714 * Fires when on first focus - needed by toolbars..
24715 * @param {HtmlEditor} this
24720 * Auto save the htmlEditor value as a file into Events
24721 * @param {HtmlEditor} this
24725 * @event savedpreview
24726 * preview the saved version of htmlEditor
24727 * @param {HtmlEditor} this
24734 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24738 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24743 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24748 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24753 * @cfg {Number} height (in pixels)
24757 * @cfg {Number} width (in pixels)
24762 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24765 stylesheets: false,
24770 // private properties
24771 validationEvent : false,
24773 initialized : false,
24776 onFocus : Roo.emptyFn,
24778 hideMode:'offsets',
24780 tbContainer : false,
24784 toolbarContainer :function() {
24785 return this.wrap.select('.x-html-editor-tb',true).first();
24789 * Protected method that will not generally be called directly. It
24790 * is called when the editor creates its toolbar. Override this method if you need to
24791 * add custom toolbar buttons.
24792 * @param {HtmlEditor} editor
24794 createToolbar : function(){
24795 Roo.log('renewing');
24796 Roo.log("create toolbars");
24798 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24799 this.toolbars[0].render(this.toolbarContainer());
24803 // if (!editor.toolbars || !editor.toolbars.length) {
24804 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24807 // for (var i =0 ; i < editor.toolbars.length;i++) {
24808 // editor.toolbars[i] = Roo.factory(
24809 // typeof(editor.toolbars[i]) == 'string' ?
24810 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24811 // Roo.bootstrap.HtmlEditor);
24812 // editor.toolbars[i].init(editor);
24818 onRender : function(ct, position)
24820 // Roo.log("Call onRender: " + this.xtype);
24822 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24824 this.wrap = this.inputEl().wrap({
24825 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24828 this.editorcore.onRender(ct, position);
24830 if (this.resizable) {
24831 this.resizeEl = new Roo.Resizable(this.wrap, {
24835 minHeight : this.height,
24836 height: this.height,
24837 handles : this.resizable,
24840 resize : function(r, w, h) {
24841 _t.onResize(w,h); // -something
24847 this.createToolbar(this);
24850 if(!this.width && this.resizable){
24851 this.setSize(this.wrap.getSize());
24853 if (this.resizeEl) {
24854 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24855 // should trigger onReize..
24861 onResize : function(w, h)
24863 Roo.log('resize: ' +w + ',' + h );
24864 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24868 if(this.inputEl() ){
24869 if(typeof w == 'number'){
24870 var aw = w - this.wrap.getFrameWidth('lr');
24871 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24874 if(typeof h == 'number'){
24875 var tbh = -11; // fixme it needs to tool bar size!
24876 for (var i =0; i < this.toolbars.length;i++) {
24877 // fixme - ask toolbars for heights?
24878 tbh += this.toolbars[i].el.getHeight();
24879 //if (this.toolbars[i].footer) {
24880 // tbh += this.toolbars[i].footer.el.getHeight();
24888 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24889 ah -= 5; // knock a few pixes off for look..
24890 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24894 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24895 this.editorcore.onResize(ew,eh);
24900 * Toggles the editor between standard and source edit mode.
24901 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24903 toggleSourceEdit : function(sourceEditMode)
24905 this.editorcore.toggleSourceEdit(sourceEditMode);
24907 if(this.editorcore.sourceEditMode){
24908 Roo.log('editor - showing textarea');
24911 // Roo.log(this.syncValue());
24913 this.inputEl().removeClass(['hide', 'x-hidden']);
24914 this.inputEl().dom.removeAttribute('tabIndex');
24915 this.inputEl().focus();
24917 Roo.log('editor - hiding textarea');
24919 // Roo.log(this.pushValue());
24922 this.inputEl().addClass(['hide', 'x-hidden']);
24923 this.inputEl().dom.setAttribute('tabIndex', -1);
24924 //this.deferFocus();
24927 if(this.resizable){
24928 this.setSize(this.wrap.getSize());
24931 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24934 // private (for BoxComponent)
24935 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24937 // private (for BoxComponent)
24938 getResizeEl : function(){
24942 // private (for BoxComponent)
24943 getPositionEl : function(){
24948 initEvents : function(){
24949 this.originalValue = this.getValue();
24953 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24956 // markInvalid : Roo.emptyFn,
24958 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24961 // clearInvalid : Roo.emptyFn,
24963 setValue : function(v){
24964 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24965 this.editorcore.pushValue();
24970 deferFocus : function(){
24971 this.focus.defer(10, this);
24975 focus : function(){
24976 this.editorcore.focus();
24982 onDestroy : function(){
24988 for (var i =0; i < this.toolbars.length;i++) {
24989 // fixme - ask toolbars for heights?
24990 this.toolbars[i].onDestroy();
24993 this.wrap.dom.innerHTML = '';
24994 this.wrap.remove();
24999 onFirstFocus : function(){
25000 //Roo.log("onFirstFocus");
25001 this.editorcore.onFirstFocus();
25002 for (var i =0; i < this.toolbars.length;i++) {
25003 this.toolbars[i].onFirstFocus();
25009 syncValue : function()
25011 this.editorcore.syncValue();
25014 pushValue : function()
25016 this.editorcore.pushValue();
25020 // hide stuff that is not compatible
25034 * @event specialkey
25038 * @cfg {String} fieldClass @hide
25041 * @cfg {String} focusClass @hide
25044 * @cfg {String} autoCreate @hide
25047 * @cfg {String} inputType @hide
25051 * @cfg {String} invalidText @hide
25054 * @cfg {String} msgFx @hide
25057 * @cfg {String} validateOnBlur @hide
25066 Roo.namespace('Roo.bootstrap.htmleditor');
25068 * @class Roo.bootstrap.HtmlEditorToolbar1
25074 new Roo.bootstrap.HtmlEditor({
25077 new Roo.bootstrap.HtmlEditorToolbar1({
25078 disable : { fonts: 1 , format: 1, ..., ... , ...],
25084 * @cfg {Object} disable List of elements to disable..
25085 * @cfg {Array} btns List of additional buttons.
25089 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25092 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25095 Roo.apply(this, config);
25097 // default disabled, based on 'good practice'..
25098 this.disable = this.disable || {};
25099 Roo.applyIf(this.disable, {
25102 specialElements : true
25104 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25106 this.editor = config.editor;
25107 this.editorcore = config.editor.editorcore;
25109 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25111 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25112 // dont call parent... till later.
25114 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25119 editorcore : false,
25124 "h1","h2","h3","h4","h5","h6",
25126 "abbr", "acronym", "address", "cite", "samp", "var",
25130 onRender : function(ct, position)
25132 // Roo.log("Call onRender: " + this.xtype);
25134 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25136 this.el.dom.style.marginBottom = '0';
25138 var editorcore = this.editorcore;
25139 var editor= this.editor;
25142 var btn = function(id,cmd , toggle, handler, html){
25144 var event = toggle ? 'toggle' : 'click';
25149 xns: Roo.bootstrap,
25153 enableToggle:toggle !== false,
25155 pressed : toggle ? false : null,
25158 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25159 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25165 // var cb_box = function...
25170 xns: Roo.bootstrap,
25175 xns: Roo.bootstrap,
25179 Roo.each(this.formats, function(f) {
25180 style.menu.items.push({
25182 xns: Roo.bootstrap,
25183 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25188 editorcore.insertTag(this.tagname);
25195 children.push(style);
25197 btn('bold',false,true);
25198 btn('italic',false,true);
25199 btn('align-left', 'justifyleft',true);
25200 btn('align-center', 'justifycenter',true);
25201 btn('align-right' , 'justifyright',true);
25202 btn('link', false, false, function(btn) {
25203 //Roo.log("create link?");
25204 var url = prompt(this.createLinkText, this.defaultLinkValue);
25205 if(url && url != 'http:/'+'/'){
25206 this.editorcore.relayCmd('createlink', url);
25209 btn('list','insertunorderedlist',true);
25210 btn('pencil', false,true, function(btn){
25212 this.toggleSourceEdit(btn.pressed);
25215 if (this.editor.btns.length > 0) {
25216 for (var i = 0; i<this.editor.btns.length; i++) {
25217 children.push(this.editor.btns[i]);
25225 xns: Roo.bootstrap,
25230 xns: Roo.bootstrap,
25235 cog.menu.items.push({
25237 xns: Roo.bootstrap,
25238 html : Clean styles,
25243 editorcore.insertTag(this.tagname);
25252 this.xtype = 'NavSimplebar';
25254 for(var i=0;i< children.length;i++) {
25256 this.buttons.add(this.addxtypeChild(children[i]));
25260 editor.on('editorevent', this.updateToolbar, this);
25262 onBtnClick : function(id)
25264 this.editorcore.relayCmd(id);
25265 this.editorcore.focus();
25269 * Protected method that will not generally be called directly. It triggers
25270 * a toolbar update by reading the markup state of the current selection in the editor.
25272 updateToolbar: function(){
25274 if(!this.editorcore.activated){
25275 this.editor.onFirstFocus(); // is this neeed?
25279 var btns = this.buttons;
25280 var doc = this.editorcore.doc;
25281 btns.get('bold').setActive(doc.queryCommandState('bold'));
25282 btns.get('italic').setActive(doc.queryCommandState('italic'));
25283 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25285 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25286 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25287 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25289 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25290 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25293 var ans = this.editorcore.getAllAncestors();
25294 if (this.formatCombo) {
25297 var store = this.formatCombo.store;
25298 this.formatCombo.setValue("");
25299 for (var i =0; i < ans.length;i++) {
25300 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25302 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25310 // hides menus... - so this cant be on a menu...
25311 Roo.bootstrap.MenuMgr.hideAll();
25313 Roo.bootstrap.MenuMgr.hideAll();
25314 //this.editorsyncValue();
25316 onFirstFocus: function() {
25317 this.buttons.each(function(item){
25321 toggleSourceEdit : function(sourceEditMode){
25324 if(sourceEditMode){
25325 Roo.log("disabling buttons");
25326 this.buttons.each( function(item){
25327 if(item.cmd != 'pencil'){
25333 Roo.log("enabling buttons");
25334 if(this.editorcore.initialized){
25335 this.buttons.each( function(item){
25341 Roo.log("calling toggole on editor");
25342 // tell the editor that it's been pressed..
25343 this.editor.toggleSourceEdit(sourceEditMode);
25353 * @class Roo.bootstrap.Table.AbstractSelectionModel
25354 * @extends Roo.util.Observable
25355 * Abstract base class for grid SelectionModels. It provides the interface that should be
25356 * implemented by descendant classes. This class should not be directly instantiated.
25359 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25360 this.locked = false;
25361 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25365 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
25366 /** @ignore Called by the grid automatically. Do not call directly. */
25367 init : function(grid){
25373 * Locks the selections.
25376 this.locked = true;
25380 * Unlocks the selections.
25382 unlock : function(){
25383 this.locked = false;
25387 * Returns true if the selections are locked.
25388 * @return {Boolean}
25390 isLocked : function(){
25391 return this.locked;
25395 initEvents : function ()
25401 * @extends Roo.bootstrap.Table.AbstractSelectionModel
25402 * @class Roo.bootstrap.Table.RowSelectionModel
25403 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25404 * It supports multiple selections and keyboard selection/navigation.
25406 * @param {Object} config
25409 Roo.bootstrap.Table.RowSelectionModel = function(config){
25410 Roo.apply(this, config);
25411 this.selections = new Roo.util.MixedCollection(false, function(o){
25416 this.lastActive = false;
25420 * @event selectionchange
25421 * Fires when the selection changes
25422 * @param {SelectionModel} this
25424 "selectionchange" : true,
25426 * @event afterselectionchange
25427 * Fires after the selection changes (eg. by key press or clicking)
25428 * @param {SelectionModel} this
25430 "afterselectionchange" : true,
25432 * @event beforerowselect
25433 * Fires when a row is selected being selected, return false to cancel.
25434 * @param {SelectionModel} this
25435 * @param {Number} rowIndex The selected index
25436 * @param {Boolean} keepExisting False if other selections will be cleared
25438 "beforerowselect" : true,
25441 * Fires when a row is selected.
25442 * @param {SelectionModel} this
25443 * @param {Number} rowIndex The selected index
25444 * @param {Roo.data.Record} r The record
25446 "rowselect" : true,
25448 * @event rowdeselect
25449 * Fires when a row is deselected.
25450 * @param {SelectionModel} this
25451 * @param {Number} rowIndex The selected index
25453 "rowdeselect" : true
25455 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25456 this.locked = false;
25459 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
25461 * @cfg {Boolean} singleSelect
25462 * True to allow selection of only one row at a time (defaults to false)
25464 singleSelect : false,
25467 initEvents : function()
25470 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25471 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
25472 //}else{ // allow click to work like normal
25473 // this.grid.on("rowclick", this.handleDragableRowClick, this);
25475 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25476 this.grid.on("rowclick", this.handleMouseDown, this);
25478 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25479 "up" : function(e){
25481 this.selectPrevious(e.shiftKey);
25482 }else if(this.last !== false && this.lastActive !== false){
25483 var last = this.last;
25484 this.selectRange(this.last, this.lastActive-1);
25485 this.grid.getView().focusRow(this.lastActive);
25486 if(last !== false){
25490 this.selectFirstRow();
25492 this.fireEvent("afterselectionchange", this);
25494 "down" : function(e){
25496 this.selectNext(e.shiftKey);
25497 }else if(this.last !== false && this.lastActive !== false){
25498 var last = this.last;
25499 this.selectRange(this.last, this.lastActive+1);
25500 this.grid.getView().focusRow(this.lastActive);
25501 if(last !== false){
25505 this.selectFirstRow();
25507 this.fireEvent("afterselectionchange", this);
25511 this.grid.store.on('load', function(){
25512 this.selections.clear();
25515 var view = this.grid.view;
25516 view.on("refresh", this.onRefresh, this);
25517 view.on("rowupdated", this.onRowUpdated, this);
25518 view.on("rowremoved", this.onRemove, this);
25523 onRefresh : function()
25525 var ds = this.grid.store, i, v = this.grid.view;
25526 var s = this.selections;
25527 s.each(function(r){
25528 if((i = ds.indexOfId(r.id)) != -1){
25537 onRemove : function(v, index, r){
25538 this.selections.remove(r);
25542 onRowUpdated : function(v, index, r){
25543 if(this.isSelected(r)){
25544 v.onRowSelect(index);
25550 * @param {Array} records The records to select
25551 * @param {Boolean} keepExisting (optional) True to keep existing selections
25553 selectRecords : function(records, keepExisting)
25556 this.clearSelections();
25558 var ds = this.grid.store;
25559 for(var i = 0, len = records.length; i < len; i++){
25560 this.selectRow(ds.indexOf(records[i]), true);
25565 * Gets the number of selected rows.
25568 getCount : function(){
25569 return this.selections.length;
25573 * Selects the first row in the grid.
25575 selectFirstRow : function(){
25580 * Select the last row.
25581 * @param {Boolean} keepExisting (optional) True to keep existing selections
25583 selectLastRow : function(keepExisting){
25584 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25585 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25589 * Selects the row immediately following the last selected row.
25590 * @param {Boolean} keepExisting (optional) True to keep existing selections
25592 selectNext : function(keepExisting)
25594 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25595 this.selectRow(this.last+1, keepExisting);
25596 this.grid.getView().focusRow(this.last);
25601 * Selects the row that precedes the last selected row.
25602 * @param {Boolean} keepExisting (optional) True to keep existing selections
25604 selectPrevious : function(keepExisting){
25606 this.selectRow(this.last-1, keepExisting);
25607 this.grid.getView().focusRow(this.last);
25612 * Returns the selected records
25613 * @return {Array} Array of selected records
25615 getSelections : function(){
25616 return [].concat(this.selections.items);
25620 * Returns the first selected record.
25623 getSelected : function(){
25624 return this.selections.itemAt(0);
25629 * Clears all selections.
25631 clearSelections : function(fast)
25637 var ds = this.grid.store;
25638 var s = this.selections;
25639 s.each(function(r){
25640 this.deselectRow(ds.indexOfId(r.id));
25644 this.selections.clear();
25651 * Selects all rows.
25653 selectAll : function(){
25657 this.selections.clear();
25658 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25659 this.selectRow(i, true);
25664 * Returns True if there is a selection.
25665 * @return {Boolean}
25667 hasSelection : function(){
25668 return this.selections.length > 0;
25672 * Returns True if the specified row is selected.
25673 * @param {Number/Record} record The record or index of the record to check
25674 * @return {Boolean}
25676 isSelected : function(index){
25677 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25678 return (r && this.selections.key(r.id) ? true : false);
25682 * Returns True if the specified record id is selected.
25683 * @param {String} id The id of record to check
25684 * @return {Boolean}
25686 isIdSelected : function(id){
25687 return (this.selections.key(id) ? true : false);
25692 handleMouseDBClick : function(e, t){
25696 handleMouseDown : function(e, t)
25698 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25699 if(this.isLocked() || rowIndex < 0 ){
25702 if(e.shiftKey && this.last !== false){
25703 var last = this.last;
25704 this.selectRange(last, rowIndex, e.ctrlKey);
25705 this.last = last; // reset the last
25709 var isSelected = this.isSelected(rowIndex);
25710 //Roo.log("select row:" + rowIndex);
25712 this.deselectRow(rowIndex);
25714 this.selectRow(rowIndex, true);
25718 if(e.button !== 0 && isSelected){
25719 alert('rowIndex 2: ' + rowIndex);
25720 view.focusRow(rowIndex);
25721 }else if(e.ctrlKey && isSelected){
25722 this.deselectRow(rowIndex);
25723 }else if(!isSelected){
25724 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25725 view.focusRow(rowIndex);
25729 this.fireEvent("afterselectionchange", this);
25732 handleDragableRowClick : function(grid, rowIndex, e)
25734 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25735 this.selectRow(rowIndex, false);
25736 grid.view.focusRow(rowIndex);
25737 this.fireEvent("afterselectionchange", this);
25742 * Selects multiple rows.
25743 * @param {Array} rows Array of the indexes of the row to select
25744 * @param {Boolean} keepExisting (optional) True to keep existing selections
25746 selectRows : function(rows, keepExisting){
25748 this.clearSelections();
25750 for(var i = 0, len = rows.length; i < len; i++){
25751 this.selectRow(rows[i], true);
25756 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25757 * @param {Number} startRow The index of the first row in the range
25758 * @param {Number} endRow The index of the last row in the range
25759 * @param {Boolean} keepExisting (optional) True to retain existing selections
25761 selectRange : function(startRow, endRow, keepExisting){
25766 this.clearSelections();
25768 if(startRow <= endRow){
25769 for(var i = startRow; i <= endRow; i++){
25770 this.selectRow(i, true);
25773 for(var i = startRow; i >= endRow; i--){
25774 this.selectRow(i, true);
25780 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25781 * @param {Number} startRow The index of the first row in the range
25782 * @param {Number} endRow The index of the last row in the range
25784 deselectRange : function(startRow, endRow, preventViewNotify){
25788 for(var i = startRow; i <= endRow; i++){
25789 this.deselectRow(i, preventViewNotify);
25795 * @param {Number} row The index of the row to select
25796 * @param {Boolean} keepExisting (optional) True to keep existing selections
25798 selectRow : function(index, keepExisting, preventViewNotify)
25800 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25803 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25804 if(!keepExisting || this.singleSelect){
25805 this.clearSelections();
25808 var r = this.grid.store.getAt(index);
25809 //console.log('selectRow - record id :' + r.id);
25811 this.selections.add(r);
25812 this.last = this.lastActive = index;
25813 if(!preventViewNotify){
25814 var proxy = new Roo.Element(
25815 this.grid.getRowDom(index)
25817 proxy.addClass('bg-info info');
25819 this.fireEvent("rowselect", this, index, r);
25820 this.fireEvent("selectionchange", this);
25826 * @param {Number} row The index of the row to deselect
25828 deselectRow : function(index, preventViewNotify)
25833 if(this.last == index){
25836 if(this.lastActive == index){
25837 this.lastActive = false;
25840 var r = this.grid.store.getAt(index);
25845 this.selections.remove(r);
25846 //.console.log('deselectRow - record id :' + r.id);
25847 if(!preventViewNotify){
25849 var proxy = new Roo.Element(
25850 this.grid.getRowDom(index)
25852 proxy.removeClass('bg-info info');
25854 this.fireEvent("rowdeselect", this, index);
25855 this.fireEvent("selectionchange", this);
25859 restoreLast : function(){
25861 this.last = this._last;
25866 acceptsNav : function(row, col, cm){
25867 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25871 onEditorKey : function(field, e){
25872 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25877 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25879 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25881 }else if(k == e.ENTER && !e.ctrlKey){
25885 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25887 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25889 }else if(k == e.ESC){
25893 g.startEditing(newCell[0], newCell[1]);
25899 * Ext JS Library 1.1.1
25900 * Copyright(c) 2006-2007, Ext JS, LLC.
25902 * Originally Released Under LGPL - original licence link has changed is not relivant.
25905 * <script type="text/javascript">
25909 * @class Roo.bootstrap.PagingToolbar
25910 * @extends Roo.bootstrap.NavSimplebar
25911 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25913 * Create a new PagingToolbar
25914 * @param {Object} config The config object
25915 * @param {Roo.data.Store} store
25917 Roo.bootstrap.PagingToolbar = function(config)
25919 // old args format still supported... - xtype is prefered..
25920 // created from xtype...
25922 this.ds = config.dataSource;
25924 if (config.store && !this.ds) {
25925 this.store= Roo.factory(config.store, Roo.data);
25926 this.ds = this.store;
25927 this.ds.xmodule = this.xmodule || false;
25930 this.toolbarItems = [];
25931 if (config.items) {
25932 this.toolbarItems = config.items;
25935 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25940 this.bind(this.ds);
25943 if (Roo.bootstrap.version == 4) {
25944 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25946 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25951 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25953 * @cfg {Roo.data.Store} dataSource
25954 * The underlying data store providing the paged data
25957 * @cfg {String/HTMLElement/Element} container
25958 * container The id or element that will contain the toolbar
25961 * @cfg {Boolean} displayInfo
25962 * True to display the displayMsg (defaults to false)
25965 * @cfg {Number} pageSize
25966 * The number of records to display per page (defaults to 20)
25970 * @cfg {String} displayMsg
25971 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25973 displayMsg : 'Displaying {0} - {1} of {2}',
25975 * @cfg {String} emptyMsg
25976 * The message to display when no records are found (defaults to "No data to display")
25978 emptyMsg : 'No data to display',
25980 * Customizable piece of the default paging text (defaults to "Page")
25983 beforePageText : "Page",
25985 * Customizable piece of the default paging text (defaults to "of %0")
25988 afterPageText : "of {0}",
25990 * Customizable piece of the default paging text (defaults to "First Page")
25993 firstText : "First Page",
25995 * Customizable piece of the default paging text (defaults to "Previous Page")
25998 prevText : "Previous Page",
26000 * Customizable piece of the default paging text (defaults to "Next Page")
26003 nextText : "Next Page",
26005 * Customizable piece of the default paging text (defaults to "Last Page")
26008 lastText : "Last Page",
26010 * Customizable piece of the default paging text (defaults to "Refresh")
26013 refreshText : "Refresh",
26017 onRender : function(ct, position)
26019 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26020 this.navgroup.parentId = this.id;
26021 this.navgroup.onRender(this.el, null);
26022 // add the buttons to the navgroup
26024 if(this.displayInfo){
26025 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26026 this.displayEl = this.el.select('.x-paging-info', true).first();
26027 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26028 // this.displayEl = navel.el.select('span',true).first();
26034 Roo.each(_this.buttons, function(e){ // this might need to use render????
26035 Roo.factory(e).render(_this.el);
26039 Roo.each(_this.toolbarItems, function(e) {
26040 _this.navgroup.addItem(e);
26044 this.first = this.navgroup.addItem({
26045 tooltip: this.firstText,
26046 cls: "prev btn-outline-secondary",
26047 html : ' <i class="fa fa-step-backward"></i>',
26049 preventDefault: true,
26050 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26053 this.prev = this.navgroup.addItem({
26054 tooltip: this.prevText,
26055 cls: "prev btn-outline-secondary",
26056 html : ' <i class="fa fa-backward"></i>',
26058 preventDefault: true,
26059 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26061 //this.addSeparator();
26064 var field = this.navgroup.addItem( {
26066 cls : 'x-paging-position btn-outline-secondary',
26068 html : this.beforePageText +
26069 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26070 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26073 this.field = field.el.select('input', true).first();
26074 this.field.on("keydown", this.onPagingKeydown, this);
26075 this.field.on("focus", function(){this.dom.select();});
26078 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26079 //this.field.setHeight(18);
26080 //this.addSeparator();
26081 this.next = this.navgroup.addItem({
26082 tooltip: this.nextText,
26083 cls: "next btn-outline-secondary",
26084 html : ' <i class="fa fa-forward"></i>',
26086 preventDefault: true,
26087 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26089 this.last = this.navgroup.addItem({
26090 tooltip: this.lastText,
26091 html : ' <i class="fa fa-step-forward"></i>',
26092 cls: "next btn-outline-secondary",
26094 preventDefault: true,
26095 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26097 //this.addSeparator();
26098 this.loading = this.navgroup.addItem({
26099 tooltip: this.refreshText,
26100 cls: "btn-outline-secondary",
26101 html : ' <i class="fa fa-refresh"></i>',
26102 preventDefault: true,
26103 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26109 updateInfo : function(){
26110 if(this.displayEl){
26111 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26112 var msg = count == 0 ?
26116 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26118 this.displayEl.update(msg);
26123 onLoad : function(ds, r, o)
26125 this.cursor = o.params.start ? o.params.start : 0;
26127 var d = this.getPageData(),
26132 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26133 this.field.dom.value = ap;
26134 this.first.setDisabled(ap == 1);
26135 this.prev.setDisabled(ap == 1);
26136 this.next.setDisabled(ap == ps);
26137 this.last.setDisabled(ap == ps);
26138 this.loading.enable();
26143 getPageData : function(){
26144 var total = this.ds.getTotalCount();
26147 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26148 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26153 onLoadError : function(){
26154 this.loading.enable();
26158 onPagingKeydown : function(e){
26159 var k = e.getKey();
26160 var d = this.getPageData();
26162 var v = this.field.dom.value, pageNum;
26163 if(!v || isNaN(pageNum = parseInt(v, 10))){
26164 this.field.dom.value = d.activePage;
26167 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26168 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26171 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))
26173 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26174 this.field.dom.value = pageNum;
26175 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26178 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26180 var v = this.field.dom.value, pageNum;
26181 var increment = (e.shiftKey) ? 10 : 1;
26182 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26185 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26186 this.field.dom.value = d.activePage;
26189 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26191 this.field.dom.value = parseInt(v, 10) + increment;
26192 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26193 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26200 beforeLoad : function(){
26202 this.loading.disable();
26207 onClick : function(which){
26216 ds.load({params:{start: 0, limit: this.pageSize}});
26219 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26222 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26225 var total = ds.getTotalCount();
26226 var extra = total % this.pageSize;
26227 var lastStart = extra ? (total - extra) : total-this.pageSize;
26228 ds.load({params:{start: lastStart, limit: this.pageSize}});
26231 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26237 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26238 * @param {Roo.data.Store} store The data store to unbind
26240 unbind : function(ds){
26241 ds.un("beforeload", this.beforeLoad, this);
26242 ds.un("load", this.onLoad, this);
26243 ds.un("loadexception", this.onLoadError, this);
26244 ds.un("remove", this.updateInfo, this);
26245 ds.un("add", this.updateInfo, this);
26246 this.ds = undefined;
26250 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26251 * @param {Roo.data.Store} store The data store to bind
26253 bind : function(ds){
26254 ds.on("beforeload", this.beforeLoad, this);
26255 ds.on("load", this.onLoad, this);
26256 ds.on("loadexception", this.onLoadError, this);
26257 ds.on("remove", this.updateInfo, this);
26258 ds.on("add", this.updateInfo, this);
26269 * @class Roo.bootstrap.MessageBar
26270 * @extends Roo.bootstrap.Component
26271 * Bootstrap MessageBar class
26272 * @cfg {String} html contents of the MessageBar
26273 * @cfg {String} weight (info | success | warning | danger) default info
26274 * @cfg {String} beforeClass insert the bar before the given class
26275 * @cfg {Boolean} closable (true | false) default false
26276 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26279 * Create a new Element
26280 * @param {Object} config The config object
26283 Roo.bootstrap.MessageBar = function(config){
26284 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26287 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26293 beforeClass: 'bootstrap-sticky-wrap',
26295 getAutoCreate : function(){
26299 cls: 'alert alert-dismissable alert-' + this.weight,
26304 html: this.html || ''
26310 cfg.cls += ' alert-messages-fixed';
26324 onRender : function(ct, position)
26326 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26329 var cfg = Roo.apply({}, this.getAutoCreate());
26333 cfg.cls += ' ' + this.cls;
26336 cfg.style = this.style;
26338 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26340 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26343 this.el.select('>button.close').on('click', this.hide, this);
26349 if (!this.rendered) {
26355 this.fireEvent('show', this);
26361 if (!this.rendered) {
26367 this.fireEvent('hide', this);
26370 update : function()
26372 // var e = this.el.dom.firstChild;
26374 // if(this.closable){
26375 // e = e.nextSibling;
26378 // e.data = this.html || '';
26380 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26396 * @class Roo.bootstrap.Graph
26397 * @extends Roo.bootstrap.Component
26398 * Bootstrap Graph class
26402 @cfg {String} graphtype bar | vbar | pie
26403 @cfg {number} g_x coodinator | centre x (pie)
26404 @cfg {number} g_y coodinator | centre y (pie)
26405 @cfg {number} g_r radius (pie)
26406 @cfg {number} g_height height of the chart (respected by all elements in the set)
26407 @cfg {number} g_width width of the chart (respected by all elements in the set)
26408 @cfg {Object} title The title of the chart
26411 -opts (object) options for the chart
26413 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26414 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26416 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.
26417 o stacked (boolean) whether or not to tread values as in a stacked bar chart
26419 o stretch (boolean)
26421 -opts (object) options for the pie
26424 o startAngle (number)
26425 o endAngle (number)
26429 * Create a new Input
26430 * @param {Object} config The config object
26433 Roo.bootstrap.Graph = function(config){
26434 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26440 * The img click event for the img.
26441 * @param {Roo.EventObject} e
26447 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
26458 //g_colors: this.colors,
26465 getAutoCreate : function(){
26476 onRender : function(ct,position){
26479 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26481 if (typeof(Raphael) == 'undefined') {
26482 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26486 this.raphael = Raphael(this.el.dom);
26488 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26489 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26490 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26491 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26493 r.text(160, 10, "Single Series Chart").attr(txtattr);
26494 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26495 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26496 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26498 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26499 r.barchart(330, 10, 300, 220, data1);
26500 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26501 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26504 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26505 // r.barchart(30, 30, 560, 250, xdata, {
26506 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26507 // axis : "0 0 1 1",
26508 // axisxlabels : xdata
26509 // //yvalues : cols,
26512 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26514 // this.load(null,xdata,{
26515 // axis : "0 0 1 1",
26516 // axisxlabels : xdata
26521 load : function(graphtype,xdata,opts)
26523 this.raphael.clear();
26525 graphtype = this.graphtype;
26530 var r = this.raphael,
26531 fin = function () {
26532 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26534 fout = function () {
26535 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26537 pfin = function() {
26538 this.sector.stop();
26539 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26542 this.label[0].stop();
26543 this.label[0].attr({ r: 7.5 });
26544 this.label[1].attr({ "font-weight": 800 });
26547 pfout = function() {
26548 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26551 this.label[0].animate({ r: 5 }, 500, "bounce");
26552 this.label[1].attr({ "font-weight": 400 });
26558 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26561 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26564 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26565 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26567 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26574 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26579 setTitle: function(o)
26584 initEvents: function() {
26587 this.el.on('click', this.onClick, this);
26591 onClick : function(e)
26593 Roo.log('img onclick');
26594 this.fireEvent('click', this, e);
26606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26609 * @class Roo.bootstrap.dash.NumberBox
26610 * @extends Roo.bootstrap.Component
26611 * Bootstrap NumberBox class
26612 * @cfg {String} headline Box headline
26613 * @cfg {String} content Box content
26614 * @cfg {String} icon Box icon
26615 * @cfg {String} footer Footer text
26616 * @cfg {String} fhref Footer href
26619 * Create a new NumberBox
26620 * @param {Object} config The config object
26624 Roo.bootstrap.dash.NumberBox = function(config){
26625 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26629 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26638 getAutoCreate : function(){
26642 cls : 'small-box ',
26650 cls : 'roo-headline',
26651 html : this.headline
26655 cls : 'roo-content',
26656 html : this.content
26670 cls : 'ion ' + this.icon
26679 cls : 'small-box-footer',
26680 href : this.fhref || '#',
26684 cfg.cn.push(footer);
26691 onRender : function(ct,position){
26692 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26699 setHeadline: function (value)
26701 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26704 setFooter: function (value, href)
26706 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26709 this.el.select('a.small-box-footer',true).first().attr('href', href);
26714 setContent: function (value)
26716 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26719 initEvents: function()
26733 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26736 * @class Roo.bootstrap.dash.TabBox
26737 * @extends Roo.bootstrap.Component
26738 * Bootstrap TabBox class
26739 * @cfg {String} title Title of the TabBox
26740 * @cfg {String} icon Icon of the TabBox
26741 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26742 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26745 * Create a new TabBox
26746 * @param {Object} config The config object
26750 Roo.bootstrap.dash.TabBox = function(config){
26751 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26756 * When a pane is added
26757 * @param {Roo.bootstrap.dash.TabPane} pane
26761 * @event activatepane
26762 * When a pane is activated
26763 * @param {Roo.bootstrap.dash.TabPane} pane
26765 "activatepane" : true
26773 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26778 tabScrollable : false,
26780 getChildContainer : function()
26782 return this.el.select('.tab-content', true).first();
26785 getAutoCreate : function(){
26789 cls: 'pull-left header',
26797 cls: 'fa ' + this.icon
26803 cls: 'nav nav-tabs pull-right',
26809 if(this.tabScrollable){
26816 cls: 'nav nav-tabs pull-right',
26827 cls: 'nav-tabs-custom',
26832 cls: 'tab-content no-padding',
26840 initEvents : function()
26842 //Roo.log('add add pane handler');
26843 this.on('addpane', this.onAddPane, this);
26846 * Updates the box title
26847 * @param {String} html to set the title to.
26849 setTitle : function(value)
26851 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26853 onAddPane : function(pane)
26855 this.panes.push(pane);
26856 //Roo.log('addpane');
26858 // tabs are rendere left to right..
26859 if(!this.showtabs){
26863 var ctr = this.el.select('.nav-tabs', true).first();
26866 var existing = ctr.select('.nav-tab',true);
26867 var qty = existing.getCount();;
26870 var tab = ctr.createChild({
26872 cls : 'nav-tab' + (qty ? '' : ' active'),
26880 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26883 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26885 pane.el.addClass('active');
26890 onTabClick : function(ev,un,ob,pane)
26892 //Roo.log('tab - prev default');
26893 ev.preventDefault();
26896 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26897 pane.tab.addClass('active');
26898 //Roo.log(pane.title);
26899 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26900 // technically we should have a deactivate event.. but maybe add later.
26901 // and it should not de-activate the selected tab...
26902 this.fireEvent('activatepane', pane);
26903 pane.el.addClass('active');
26904 pane.fireEvent('activate');
26909 getActivePane : function()
26912 Roo.each(this.panes, function(p) {
26913 if(p.el.hasClass('active')){
26934 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26936 * @class Roo.bootstrap.TabPane
26937 * @extends Roo.bootstrap.Component
26938 * Bootstrap TabPane class
26939 * @cfg {Boolean} active (false | true) Default false
26940 * @cfg {String} title title of panel
26944 * Create a new TabPane
26945 * @param {Object} config The config object
26948 Roo.bootstrap.dash.TabPane = function(config){
26949 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26955 * When a pane is activated
26956 * @param {Roo.bootstrap.dash.TabPane} pane
26963 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26968 // the tabBox that this is attached to.
26971 getAutoCreate : function()
26979 cfg.cls += ' active';
26984 initEvents : function()
26986 //Roo.log('trigger add pane handler');
26987 this.parent().fireEvent('addpane', this)
26991 * Updates the tab title
26992 * @param {String} html to set the title to.
26994 setTitle: function(str)
27000 this.tab.select('a', true).first().dom.innerHTML = str;
27017 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27020 * @class Roo.bootstrap.menu.Menu
27021 * @extends Roo.bootstrap.Component
27022 * Bootstrap Menu class - container for Menu
27023 * @cfg {String} html Text of the menu
27024 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27025 * @cfg {String} icon Font awesome icon
27026 * @cfg {String} pos Menu align to (top | bottom) default bottom
27030 * Create a new Menu
27031 * @param {Object} config The config object
27035 Roo.bootstrap.menu.Menu = function(config){
27036 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27040 * @event beforeshow
27041 * Fires before this menu is displayed
27042 * @param {Roo.bootstrap.menu.Menu} this
27046 * @event beforehide
27047 * Fires before this menu is hidden
27048 * @param {Roo.bootstrap.menu.Menu} this
27053 * Fires after this menu is displayed
27054 * @param {Roo.bootstrap.menu.Menu} this
27059 * Fires after this menu is hidden
27060 * @param {Roo.bootstrap.menu.Menu} this
27065 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27066 * @param {Roo.bootstrap.menu.Menu} this
27067 * @param {Roo.EventObject} e
27074 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27078 weight : 'default',
27083 getChildContainer : function() {
27084 if(this.isSubMenu){
27088 return this.el.select('ul.dropdown-menu', true).first();
27091 getAutoCreate : function()
27096 cls : 'roo-menu-text',
27104 cls : 'fa ' + this.icon
27115 cls : 'dropdown-button btn btn-' + this.weight,
27120 cls : 'dropdown-toggle btn btn-' + this.weight,
27130 cls : 'dropdown-menu'
27136 if(this.pos == 'top'){
27137 cfg.cls += ' dropup';
27140 if(this.isSubMenu){
27143 cls : 'dropdown-menu'
27150 onRender : function(ct, position)
27152 this.isSubMenu = ct.hasClass('dropdown-submenu');
27154 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27157 initEvents : function()
27159 if(this.isSubMenu){
27163 this.hidden = true;
27165 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27166 this.triggerEl.on('click', this.onTriggerPress, this);
27168 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27169 this.buttonEl.on('click', this.onClick, this);
27175 if(this.isSubMenu){
27179 return this.el.select('ul.dropdown-menu', true).first();
27182 onClick : function(e)
27184 this.fireEvent("click", this, e);
27187 onTriggerPress : function(e)
27189 if (this.isVisible()) {
27196 isVisible : function(){
27197 return !this.hidden;
27202 this.fireEvent("beforeshow", this);
27204 this.hidden = false;
27205 this.el.addClass('open');
27207 Roo.get(document).on("mouseup", this.onMouseUp, this);
27209 this.fireEvent("show", this);
27216 this.fireEvent("beforehide", this);
27218 this.hidden = true;
27219 this.el.removeClass('open');
27221 Roo.get(document).un("mouseup", this.onMouseUp);
27223 this.fireEvent("hide", this);
27226 onMouseUp : function()
27240 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27243 * @class Roo.bootstrap.menu.Item
27244 * @extends Roo.bootstrap.Component
27245 * Bootstrap MenuItem class
27246 * @cfg {Boolean} submenu (true | false) default false
27247 * @cfg {String} html text of the item
27248 * @cfg {String} href the link
27249 * @cfg {Boolean} disable (true | false) default false
27250 * @cfg {Boolean} preventDefault (true | false) default true
27251 * @cfg {String} icon Font awesome icon
27252 * @cfg {String} pos Submenu align to (left | right) default right
27256 * Create a new Item
27257 * @param {Object} config The config object
27261 Roo.bootstrap.menu.Item = function(config){
27262 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27266 * Fires when the mouse is hovering over this menu
27267 * @param {Roo.bootstrap.menu.Item} this
27268 * @param {Roo.EventObject} e
27273 * Fires when the mouse exits this menu
27274 * @param {Roo.bootstrap.menu.Item} this
27275 * @param {Roo.EventObject} e
27281 * The raw click event for the entire grid.
27282 * @param {Roo.EventObject} e
27288 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27293 preventDefault: true,
27298 getAutoCreate : function()
27303 cls : 'roo-menu-item-text',
27311 cls : 'fa ' + this.icon
27320 href : this.href || '#',
27327 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27331 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27333 if(this.pos == 'left'){
27334 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27341 initEvents : function()
27343 this.el.on('mouseover', this.onMouseOver, this);
27344 this.el.on('mouseout', this.onMouseOut, this);
27346 this.el.select('a', true).first().on('click', this.onClick, this);
27350 onClick : function(e)
27352 if(this.preventDefault){
27353 e.preventDefault();
27356 this.fireEvent("click", this, e);
27359 onMouseOver : function(e)
27361 if(this.submenu && this.pos == 'left'){
27362 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27365 this.fireEvent("mouseover", this, e);
27368 onMouseOut : function(e)
27370 this.fireEvent("mouseout", this, e);
27382 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27385 * @class Roo.bootstrap.menu.Separator
27386 * @extends Roo.bootstrap.Component
27387 * Bootstrap Separator class
27390 * Create a new Separator
27391 * @param {Object} config The config object
27395 Roo.bootstrap.menu.Separator = function(config){
27396 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27399 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
27401 getAutoCreate : function(){
27422 * @class Roo.bootstrap.Tooltip
27423 * Bootstrap Tooltip class
27424 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27425 * to determine which dom element triggers the tooltip.
27427 * It needs to add support for additional attributes like tooltip-position
27430 * Create a new Toolti
27431 * @param {Object} config The config object
27434 Roo.bootstrap.Tooltip = function(config){
27435 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27437 this.alignment = Roo.bootstrap.Tooltip.alignment;
27439 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27440 this.alignment = config.alignment;
27445 Roo.apply(Roo.bootstrap.Tooltip, {
27447 * @function init initialize tooltip monitoring.
27451 currentTip : false,
27452 currentRegion : false,
27458 Roo.get(document).on('mouseover', this.enter ,this);
27459 Roo.get(document).on('mouseout', this.leave, this);
27462 this.currentTip = new Roo.bootstrap.Tooltip();
27465 enter : function(ev)
27467 var dom = ev.getTarget();
27469 //Roo.log(['enter',dom]);
27470 var el = Roo.fly(dom);
27471 if (this.currentEl) {
27473 //Roo.log(this.currentEl);
27474 //Roo.log(this.currentEl.contains(dom));
27475 if (this.currentEl == el) {
27478 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27484 if (this.currentTip.el) {
27485 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27489 if(!el || el.dom == document){
27495 // you can not look for children, as if el is the body.. then everythign is the child..
27496 if (!el.attr('tooltip')) { //
27497 if (!el.select("[tooltip]").elements.length) {
27500 // is the mouse over this child...?
27501 bindEl = el.select("[tooltip]").first();
27502 var xy = ev.getXY();
27503 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27504 //Roo.log("not in region.");
27507 //Roo.log("child element over..");
27510 this.currentEl = bindEl;
27511 this.currentTip.bind(bindEl);
27512 this.currentRegion = Roo.lib.Region.getRegion(dom);
27513 this.currentTip.enter();
27516 leave : function(ev)
27518 var dom = ev.getTarget();
27519 //Roo.log(['leave',dom]);
27520 if (!this.currentEl) {
27525 if (dom != this.currentEl.dom) {
27528 var xy = ev.getXY();
27529 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
27532 // only activate leave if mouse cursor is outside... bounding box..
27537 if (this.currentTip) {
27538 this.currentTip.leave();
27540 //Roo.log('clear currentEl');
27541 this.currentEl = false;
27546 'left' : ['r-l', [-2,0], 'right'],
27547 'right' : ['l-r', [2,0], 'left'],
27548 'bottom' : ['t-b', [0,2], 'top'],
27549 'top' : [ 'b-t', [0,-2], 'bottom']
27555 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27560 delay : null, // can be { show : 300 , hide: 500}
27564 hoverState : null, //???
27566 placement : 'bottom',
27570 getAutoCreate : function(){
27577 cls : 'tooltip-arrow'
27580 cls : 'tooltip-inner'
27587 bind : function(el)
27593 enter : function () {
27595 if (this.timeout != null) {
27596 clearTimeout(this.timeout);
27599 this.hoverState = 'in';
27600 //Roo.log("enter - show");
27601 if (!this.delay || !this.delay.show) {
27606 this.timeout = setTimeout(function () {
27607 if (_t.hoverState == 'in') {
27610 }, this.delay.show);
27614 clearTimeout(this.timeout);
27616 this.hoverState = 'out';
27617 if (!this.delay || !this.delay.hide) {
27623 this.timeout = setTimeout(function () {
27624 //Roo.log("leave - timeout");
27626 if (_t.hoverState == 'out') {
27628 Roo.bootstrap.Tooltip.currentEl = false;
27633 show : function (msg)
27636 this.render(document.body);
27639 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27641 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27643 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27645 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27647 var placement = typeof this.placement == 'function' ?
27648 this.placement.call(this, this.el, on_el) :
27651 var autoToken = /\s?auto?\s?/i;
27652 var autoPlace = autoToken.test(placement);
27654 placement = placement.replace(autoToken, '') || 'top';
27658 //this.el.setXY([0,0]);
27660 //this.el.dom.style.display='block';
27662 //this.el.appendTo(on_el);
27664 var p = this.getPosition();
27665 var box = this.el.getBox();
27671 var align = this.alignment[placement];
27673 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27675 if(placement == 'top' || placement == 'bottom'){
27677 placement = 'right';
27680 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27681 placement = 'left';
27684 var scroll = Roo.select('body', true).first().getScroll();
27686 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27690 align = this.alignment[placement];
27693 this.el.alignTo(this.bindEl, align[0],align[1]);
27694 //var arrow = this.el.select('.arrow',true).first();
27695 //arrow.set(align[2],
27697 this.el.addClass(placement);
27699 this.el.addClass('in fade');
27701 this.hoverState = null;
27703 if (this.el.hasClass('fade')) {
27714 //this.el.setXY([0,0]);
27715 this.el.removeClass('in');
27731 * @class Roo.bootstrap.LocationPicker
27732 * @extends Roo.bootstrap.Component
27733 * Bootstrap LocationPicker class
27734 * @cfg {Number} latitude Position when init default 0
27735 * @cfg {Number} longitude Position when init default 0
27736 * @cfg {Number} zoom default 15
27737 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27738 * @cfg {Boolean} mapTypeControl default false
27739 * @cfg {Boolean} disableDoubleClickZoom default false
27740 * @cfg {Boolean} scrollwheel default true
27741 * @cfg {Boolean} streetViewControl default false
27742 * @cfg {Number} radius default 0
27743 * @cfg {String} locationName
27744 * @cfg {Boolean} draggable default true
27745 * @cfg {Boolean} enableAutocomplete default false
27746 * @cfg {Boolean} enableReverseGeocode default true
27747 * @cfg {String} markerTitle
27750 * Create a new LocationPicker
27751 * @param {Object} config The config object
27755 Roo.bootstrap.LocationPicker = function(config){
27757 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27762 * Fires when the picker initialized.
27763 * @param {Roo.bootstrap.LocationPicker} this
27764 * @param {Google Location} location
27768 * @event positionchanged
27769 * Fires when the picker position changed.
27770 * @param {Roo.bootstrap.LocationPicker} this
27771 * @param {Google Location} location
27773 positionchanged : true,
27776 * Fires when the map resize.
27777 * @param {Roo.bootstrap.LocationPicker} this
27782 * Fires when the map show.
27783 * @param {Roo.bootstrap.LocationPicker} this
27788 * Fires when the map hide.
27789 * @param {Roo.bootstrap.LocationPicker} this
27794 * Fires when click the map.
27795 * @param {Roo.bootstrap.LocationPicker} this
27796 * @param {Map event} e
27800 * @event mapRightClick
27801 * Fires when right click the map.
27802 * @param {Roo.bootstrap.LocationPicker} this
27803 * @param {Map event} e
27805 mapRightClick : true,
27807 * @event markerClick
27808 * Fires when click the marker.
27809 * @param {Roo.bootstrap.LocationPicker} this
27810 * @param {Map event} e
27812 markerClick : true,
27814 * @event markerRightClick
27815 * Fires when right click the marker.
27816 * @param {Roo.bootstrap.LocationPicker} this
27817 * @param {Map event} e
27819 markerRightClick : true,
27821 * @event OverlayViewDraw
27822 * Fires when OverlayView Draw
27823 * @param {Roo.bootstrap.LocationPicker} this
27825 OverlayViewDraw : true,
27827 * @event OverlayViewOnAdd
27828 * Fires when OverlayView Draw
27829 * @param {Roo.bootstrap.LocationPicker} this
27831 OverlayViewOnAdd : true,
27833 * @event OverlayViewOnRemove
27834 * Fires when OverlayView Draw
27835 * @param {Roo.bootstrap.LocationPicker} this
27837 OverlayViewOnRemove : true,
27839 * @event OverlayViewShow
27840 * Fires when OverlayView Draw
27841 * @param {Roo.bootstrap.LocationPicker} this
27842 * @param {Pixel} cpx
27844 OverlayViewShow : true,
27846 * @event OverlayViewHide
27847 * Fires when OverlayView Draw
27848 * @param {Roo.bootstrap.LocationPicker} this
27850 OverlayViewHide : true,
27852 * @event loadexception
27853 * Fires when load google lib failed.
27854 * @param {Roo.bootstrap.LocationPicker} this
27856 loadexception : true
27861 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27863 gMapContext: false,
27869 mapTypeControl: false,
27870 disableDoubleClickZoom: false,
27872 streetViewControl: false,
27876 enableAutocomplete: false,
27877 enableReverseGeocode: true,
27880 getAutoCreate: function()
27885 cls: 'roo-location-picker'
27891 initEvents: function(ct, position)
27893 if(!this.el.getWidth() || this.isApplied()){
27897 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27902 initial: function()
27904 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27905 this.fireEvent('loadexception', this);
27909 if(!this.mapTypeId){
27910 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27913 this.gMapContext = this.GMapContext();
27915 this.initOverlayView();
27917 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27921 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27922 _this.setPosition(_this.gMapContext.marker.position);
27925 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27926 _this.fireEvent('mapClick', this, event);
27930 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27931 _this.fireEvent('mapRightClick', this, event);
27935 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27936 _this.fireEvent('markerClick', this, event);
27940 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27941 _this.fireEvent('markerRightClick', this, event);
27945 this.setPosition(this.gMapContext.location);
27947 this.fireEvent('initial', this, this.gMapContext.location);
27950 initOverlayView: function()
27954 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27958 _this.fireEvent('OverlayViewDraw', _this);
27963 _this.fireEvent('OverlayViewOnAdd', _this);
27966 onRemove: function()
27968 _this.fireEvent('OverlayViewOnRemove', _this);
27971 show: function(cpx)
27973 _this.fireEvent('OverlayViewShow', _this, cpx);
27978 _this.fireEvent('OverlayViewHide', _this);
27984 fromLatLngToContainerPixel: function(event)
27986 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27989 isApplied: function()
27991 return this.getGmapContext() == false ? false : true;
27994 getGmapContext: function()
27996 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27999 GMapContext: function()
28001 var position = new google.maps.LatLng(this.latitude, this.longitude);
28003 var _map = new google.maps.Map(this.el.dom, {
28006 mapTypeId: this.mapTypeId,
28007 mapTypeControl: this.mapTypeControl,
28008 disableDoubleClickZoom: this.disableDoubleClickZoom,
28009 scrollwheel: this.scrollwheel,
28010 streetViewControl: this.streetViewControl,
28011 locationName: this.locationName,
28012 draggable: this.draggable,
28013 enableAutocomplete: this.enableAutocomplete,
28014 enableReverseGeocode: this.enableReverseGeocode
28017 var _marker = new google.maps.Marker({
28018 position: position,
28020 title: this.markerTitle,
28021 draggable: this.draggable
28028 location: position,
28029 radius: this.radius,
28030 locationName: this.locationName,
28031 addressComponents: {
28032 formatted_address: null,
28033 addressLine1: null,
28034 addressLine2: null,
28036 streetNumber: null,
28040 stateOrProvince: null
28043 domContainer: this.el.dom,
28044 geodecoder: new google.maps.Geocoder()
28048 drawCircle: function(center, radius, options)
28050 if (this.gMapContext.circle != null) {
28051 this.gMapContext.circle.setMap(null);
28055 options = Roo.apply({}, options, {
28056 strokeColor: "#0000FF",
28057 strokeOpacity: .35,
28059 fillColor: "#0000FF",
28063 options.map = this.gMapContext.map;
28064 options.radius = radius;
28065 options.center = center;
28066 this.gMapContext.circle = new google.maps.Circle(options);
28067 return this.gMapContext.circle;
28073 setPosition: function(location)
28075 this.gMapContext.location = location;
28076 this.gMapContext.marker.setPosition(location);
28077 this.gMapContext.map.panTo(location);
28078 this.drawCircle(location, this.gMapContext.radius, {});
28082 if (this.gMapContext.settings.enableReverseGeocode) {
28083 this.gMapContext.geodecoder.geocode({
28084 latLng: this.gMapContext.location
28085 }, function(results, status) {
28087 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28088 _this.gMapContext.locationName = results[0].formatted_address;
28089 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28091 _this.fireEvent('positionchanged', this, location);
28098 this.fireEvent('positionchanged', this, location);
28103 google.maps.event.trigger(this.gMapContext.map, "resize");
28105 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28107 this.fireEvent('resize', this);
28110 setPositionByLatLng: function(latitude, longitude)
28112 this.setPosition(new google.maps.LatLng(latitude, longitude));
28115 getCurrentPosition: function()
28118 latitude: this.gMapContext.location.lat(),
28119 longitude: this.gMapContext.location.lng()
28123 getAddressName: function()
28125 return this.gMapContext.locationName;
28128 getAddressComponents: function()
28130 return this.gMapContext.addressComponents;
28133 address_component_from_google_geocode: function(address_components)
28137 for (var i = 0; i < address_components.length; i++) {
28138 var component = address_components[i];
28139 if (component.types.indexOf("postal_code") >= 0) {
28140 result.postalCode = component.short_name;
28141 } else if (component.types.indexOf("street_number") >= 0) {
28142 result.streetNumber = component.short_name;
28143 } else if (component.types.indexOf("route") >= 0) {
28144 result.streetName = component.short_name;
28145 } else if (component.types.indexOf("neighborhood") >= 0) {
28146 result.city = component.short_name;
28147 } else if (component.types.indexOf("locality") >= 0) {
28148 result.city = component.short_name;
28149 } else if (component.types.indexOf("sublocality") >= 0) {
28150 result.district = component.short_name;
28151 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28152 result.stateOrProvince = component.short_name;
28153 } else if (component.types.indexOf("country") >= 0) {
28154 result.country = component.short_name;
28158 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28159 result.addressLine2 = "";
28163 setZoomLevel: function(zoom)
28165 this.gMapContext.map.setZoom(zoom);
28178 this.fireEvent('show', this);
28189 this.fireEvent('hide', this);
28194 Roo.apply(Roo.bootstrap.LocationPicker, {
28196 OverlayView : function(map, options)
28198 options = options || {};
28205 * @class Roo.bootstrap.Alert
28206 * @extends Roo.bootstrap.Component
28207 * Bootstrap Alert class - shows an alert area box
28209 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28210 Enter a valid email address
28213 * @cfg {String} title The title of alert
28214 * @cfg {String} html The content of alert
28215 * @cfg {String} weight ( success | info | warning | danger )
28216 * @cfg {String} faicon font-awesomeicon
28219 * Create a new alert
28220 * @param {Object} config The config object
28224 Roo.bootstrap.Alert = function(config){
28225 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28229 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28236 getAutoCreate : function()
28245 cls : 'roo-alert-icon'
28250 cls : 'roo-alert-title',
28255 cls : 'roo-alert-text',
28262 cfg.cn[0].cls += ' fa ' + this.faicon;
28266 cfg.cls += ' alert-' + this.weight;
28272 initEvents: function()
28274 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28277 setTitle : function(str)
28279 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28282 setText : function(str)
28284 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28287 setWeight : function(weight)
28290 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28293 this.weight = weight;
28295 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28298 setIcon : function(icon)
28301 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28304 this.faicon = icon;
28306 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28327 * @class Roo.bootstrap.UploadCropbox
28328 * @extends Roo.bootstrap.Component
28329 * Bootstrap UploadCropbox class
28330 * @cfg {String} emptyText show when image has been loaded
28331 * @cfg {String} rotateNotify show when image too small to rotate
28332 * @cfg {Number} errorTimeout default 3000
28333 * @cfg {Number} minWidth default 300
28334 * @cfg {Number} minHeight default 300
28335 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28336 * @cfg {Boolean} isDocument (true|false) default false
28337 * @cfg {String} url action url
28338 * @cfg {String} paramName default 'imageUpload'
28339 * @cfg {String} method default POST
28340 * @cfg {Boolean} loadMask (true|false) default true
28341 * @cfg {Boolean} loadingText default 'Loading...'
28344 * Create a new UploadCropbox
28345 * @param {Object} config The config object
28348 Roo.bootstrap.UploadCropbox = function(config){
28349 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28353 * @event beforeselectfile
28354 * Fire before select file
28355 * @param {Roo.bootstrap.UploadCropbox} this
28357 "beforeselectfile" : true,
28360 * Fire after initEvent
28361 * @param {Roo.bootstrap.UploadCropbox} this
28366 * Fire after initEvent
28367 * @param {Roo.bootstrap.UploadCropbox} this
28368 * @param {String} data
28373 * Fire when preparing the file data
28374 * @param {Roo.bootstrap.UploadCropbox} this
28375 * @param {Object} file
28380 * Fire when get exception
28381 * @param {Roo.bootstrap.UploadCropbox} this
28382 * @param {XMLHttpRequest} xhr
28384 "exception" : true,
28386 * @event beforeloadcanvas
28387 * Fire before load the canvas
28388 * @param {Roo.bootstrap.UploadCropbox} this
28389 * @param {String} src
28391 "beforeloadcanvas" : true,
28394 * Fire when trash image
28395 * @param {Roo.bootstrap.UploadCropbox} this
28400 * Fire when download the image
28401 * @param {Roo.bootstrap.UploadCropbox} this
28405 * @event footerbuttonclick
28406 * Fire when footerbuttonclick
28407 * @param {Roo.bootstrap.UploadCropbox} this
28408 * @param {String} type
28410 "footerbuttonclick" : true,
28414 * @param {Roo.bootstrap.UploadCropbox} this
28419 * Fire when rotate the image
28420 * @param {Roo.bootstrap.UploadCropbox} this
28421 * @param {String} pos
28426 * Fire when inspect the file
28427 * @param {Roo.bootstrap.UploadCropbox} this
28428 * @param {Object} file
28433 * Fire when xhr upload the file
28434 * @param {Roo.bootstrap.UploadCropbox} this
28435 * @param {Object} data
28440 * Fire when arrange the file data
28441 * @param {Roo.bootstrap.UploadCropbox} this
28442 * @param {Object} formData
28447 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28450 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
28452 emptyText : 'Click to upload image',
28453 rotateNotify : 'Image is too small to rotate',
28454 errorTimeout : 3000,
28468 cropType : 'image/jpeg',
28470 canvasLoaded : false,
28471 isDocument : false,
28473 paramName : 'imageUpload',
28475 loadingText : 'Loading...',
28478 getAutoCreate : function()
28482 cls : 'roo-upload-cropbox',
28486 cls : 'roo-upload-cropbox-selector',
28491 cls : 'roo-upload-cropbox-body',
28492 style : 'cursor:pointer',
28496 cls : 'roo-upload-cropbox-preview'
28500 cls : 'roo-upload-cropbox-thumb'
28504 cls : 'roo-upload-cropbox-empty-notify',
28505 html : this.emptyText
28509 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28510 html : this.rotateNotify
28516 cls : 'roo-upload-cropbox-footer',
28519 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28529 onRender : function(ct, position)
28531 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28533 if (this.buttons.length) {
28535 Roo.each(this.buttons, function(bb) {
28537 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28539 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28545 this.maskEl = this.el;
28549 initEvents : function()
28551 this.urlAPI = (window.createObjectURL && window) ||
28552 (window.URL && URL.revokeObjectURL && URL) ||
28553 (window.webkitURL && webkitURL);
28555 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28556 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28558 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28559 this.selectorEl.hide();
28561 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28562 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28564 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28565 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28566 this.thumbEl.hide();
28568 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28569 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28571 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28572 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28573 this.errorEl.hide();
28575 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28576 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28577 this.footerEl.hide();
28579 this.setThumbBoxSize();
28585 this.fireEvent('initial', this);
28592 window.addEventListener("resize", function() { _this.resize(); } );
28594 this.bodyEl.on('click', this.beforeSelectFile, this);
28597 this.bodyEl.on('touchstart', this.onTouchStart, this);
28598 this.bodyEl.on('touchmove', this.onTouchMove, this);
28599 this.bodyEl.on('touchend', this.onTouchEnd, this);
28603 this.bodyEl.on('mousedown', this.onMouseDown, this);
28604 this.bodyEl.on('mousemove', this.onMouseMove, this);
28605 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28606 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28607 Roo.get(document).on('mouseup', this.onMouseUp, this);
28610 this.selectorEl.on('change', this.onFileSelected, this);
28616 this.baseScale = 1;
28618 this.baseRotate = 1;
28619 this.dragable = false;
28620 this.pinching = false;
28623 this.cropData = false;
28624 this.notifyEl.dom.innerHTML = this.emptyText;
28626 this.selectorEl.dom.value = '';
28630 resize : function()
28632 if(this.fireEvent('resize', this) != false){
28633 this.setThumbBoxPosition();
28634 this.setCanvasPosition();
28638 onFooterButtonClick : function(e, el, o, type)
28641 case 'rotate-left' :
28642 this.onRotateLeft(e);
28644 case 'rotate-right' :
28645 this.onRotateRight(e);
28648 this.beforeSelectFile(e);
28663 this.fireEvent('footerbuttonclick', this, type);
28666 beforeSelectFile : function(e)
28668 e.preventDefault();
28670 if(this.fireEvent('beforeselectfile', this) != false){
28671 this.selectorEl.dom.click();
28675 onFileSelected : function(e)
28677 e.preventDefault();
28679 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28683 var file = this.selectorEl.dom.files[0];
28685 if(this.fireEvent('inspect', this, file) != false){
28686 this.prepare(file);
28691 trash : function(e)
28693 this.fireEvent('trash', this);
28696 download : function(e)
28698 this.fireEvent('download', this);
28701 loadCanvas : function(src)
28703 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28707 this.imageEl = document.createElement('img');
28711 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28713 this.imageEl.src = src;
28717 onLoadCanvas : function()
28719 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28720 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28722 this.bodyEl.un('click', this.beforeSelectFile, this);
28724 this.notifyEl.hide();
28725 this.thumbEl.show();
28726 this.footerEl.show();
28728 this.baseRotateLevel();
28730 if(this.isDocument){
28731 this.setThumbBoxSize();
28734 this.setThumbBoxPosition();
28736 this.baseScaleLevel();
28742 this.canvasLoaded = true;
28745 this.maskEl.unmask();
28750 setCanvasPosition : function()
28752 if(!this.canvasEl){
28756 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28757 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28759 this.previewEl.setLeft(pw);
28760 this.previewEl.setTop(ph);
28764 onMouseDown : function(e)
28768 this.dragable = true;
28769 this.pinching = false;
28771 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28772 this.dragable = false;
28776 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28777 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28781 onMouseMove : function(e)
28785 if(!this.canvasLoaded){
28789 if (!this.dragable){
28793 var minX = Math.ceil(this.thumbEl.getLeft(true));
28794 var minY = Math.ceil(this.thumbEl.getTop(true));
28796 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28797 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28799 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28800 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28802 x = x - this.mouseX;
28803 y = y - this.mouseY;
28805 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28806 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28808 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28809 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28811 this.previewEl.setLeft(bgX);
28812 this.previewEl.setTop(bgY);
28814 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28815 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28818 onMouseUp : function(e)
28822 this.dragable = false;
28825 onMouseWheel : function(e)
28829 this.startScale = this.scale;
28831 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28833 if(!this.zoomable()){
28834 this.scale = this.startScale;
28843 zoomable : function()
28845 var minScale = this.thumbEl.getWidth() / this.minWidth;
28847 if(this.minWidth < this.minHeight){
28848 minScale = this.thumbEl.getHeight() / this.minHeight;
28851 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28852 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28856 (this.rotate == 0 || this.rotate == 180) &&
28858 width > this.imageEl.OriginWidth ||
28859 height > this.imageEl.OriginHeight ||
28860 (width < this.minWidth && height < this.minHeight)
28868 (this.rotate == 90 || this.rotate == 270) &&
28870 width > this.imageEl.OriginWidth ||
28871 height > this.imageEl.OriginHeight ||
28872 (width < this.minHeight && height < this.minWidth)
28879 !this.isDocument &&
28880 (this.rotate == 0 || this.rotate == 180) &&
28882 width < this.minWidth ||
28883 width > this.imageEl.OriginWidth ||
28884 height < this.minHeight ||
28885 height > this.imageEl.OriginHeight
28892 !this.isDocument &&
28893 (this.rotate == 90 || this.rotate == 270) &&
28895 width < this.minHeight ||
28896 width > this.imageEl.OriginWidth ||
28897 height < this.minWidth ||
28898 height > this.imageEl.OriginHeight
28908 onRotateLeft : function(e)
28910 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28912 var minScale = this.thumbEl.getWidth() / this.minWidth;
28914 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28915 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28917 this.startScale = this.scale;
28919 while (this.getScaleLevel() < minScale){
28921 this.scale = this.scale + 1;
28923 if(!this.zoomable()){
28928 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28929 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28934 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28941 this.scale = this.startScale;
28943 this.onRotateFail();
28948 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28950 if(this.isDocument){
28951 this.setThumbBoxSize();
28952 this.setThumbBoxPosition();
28953 this.setCanvasPosition();
28958 this.fireEvent('rotate', this, 'left');
28962 onRotateRight : function(e)
28964 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28966 var minScale = this.thumbEl.getWidth() / this.minWidth;
28968 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28969 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28971 this.startScale = this.scale;
28973 while (this.getScaleLevel() < minScale){
28975 this.scale = this.scale + 1;
28977 if(!this.zoomable()){
28982 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28983 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28988 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28995 this.scale = this.startScale;
28997 this.onRotateFail();
29002 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29004 if(this.isDocument){
29005 this.setThumbBoxSize();
29006 this.setThumbBoxPosition();
29007 this.setCanvasPosition();
29012 this.fireEvent('rotate', this, 'right');
29015 onRotateFail : function()
29017 this.errorEl.show(true);
29021 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29026 this.previewEl.dom.innerHTML = '';
29028 var canvasEl = document.createElement("canvas");
29030 var contextEl = canvasEl.getContext("2d");
29032 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29033 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29034 var center = this.imageEl.OriginWidth / 2;
29036 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29037 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29038 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29039 center = this.imageEl.OriginHeight / 2;
29042 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29044 contextEl.translate(center, center);
29045 contextEl.rotate(this.rotate * Math.PI / 180);
29047 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29049 this.canvasEl = document.createElement("canvas");
29051 this.contextEl = this.canvasEl.getContext("2d");
29053 switch (this.rotate) {
29056 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29057 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29059 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29064 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29065 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29067 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29068 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);
29072 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29077 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29078 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29080 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29081 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);
29085 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);
29090 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29091 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29093 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29094 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29098 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);
29105 this.previewEl.appendChild(this.canvasEl);
29107 this.setCanvasPosition();
29112 if(!this.canvasLoaded){
29116 var imageCanvas = document.createElement("canvas");
29118 var imageContext = imageCanvas.getContext("2d");
29120 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29121 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29123 var center = imageCanvas.width / 2;
29125 imageContext.translate(center, center);
29127 imageContext.rotate(this.rotate * Math.PI / 180);
29129 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29131 var canvas = document.createElement("canvas");
29133 var context = canvas.getContext("2d");
29135 canvas.width = this.minWidth;
29136 canvas.height = this.minHeight;
29138 switch (this.rotate) {
29141 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29142 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29144 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29145 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29147 var targetWidth = this.minWidth - 2 * x;
29148 var targetHeight = this.minHeight - 2 * y;
29152 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29153 scale = targetWidth / width;
29156 if(x > 0 && y == 0){
29157 scale = targetHeight / height;
29160 if(x > 0 && y > 0){
29161 scale = targetWidth / width;
29163 if(width < height){
29164 scale = targetHeight / height;
29168 context.scale(scale, scale);
29170 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29171 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29173 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29174 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29176 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29181 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29182 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29184 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29185 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29187 var targetWidth = this.minWidth - 2 * x;
29188 var targetHeight = this.minHeight - 2 * y;
29192 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29193 scale = targetWidth / width;
29196 if(x > 0 && y == 0){
29197 scale = targetHeight / height;
29200 if(x > 0 && y > 0){
29201 scale = targetWidth / width;
29203 if(width < height){
29204 scale = targetHeight / height;
29208 context.scale(scale, scale);
29210 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29211 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29213 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29214 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29216 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29218 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29223 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29224 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29226 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29227 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29229 var targetWidth = this.minWidth - 2 * x;
29230 var targetHeight = this.minHeight - 2 * y;
29234 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29235 scale = targetWidth / width;
29238 if(x > 0 && y == 0){
29239 scale = targetHeight / height;
29242 if(x > 0 && y > 0){
29243 scale = targetWidth / width;
29245 if(width < height){
29246 scale = targetHeight / height;
29250 context.scale(scale, scale);
29252 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29253 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29255 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29256 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29258 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29259 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29261 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29266 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29267 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29269 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29270 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29272 var targetWidth = this.minWidth - 2 * x;
29273 var targetHeight = this.minHeight - 2 * y;
29277 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29278 scale = targetWidth / width;
29281 if(x > 0 && y == 0){
29282 scale = targetHeight / height;
29285 if(x > 0 && y > 0){
29286 scale = targetWidth / width;
29288 if(width < height){
29289 scale = targetHeight / height;
29293 context.scale(scale, scale);
29295 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29296 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29298 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29299 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29301 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29303 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29310 this.cropData = canvas.toDataURL(this.cropType);
29312 if(this.fireEvent('crop', this, this.cropData) !== false){
29313 this.process(this.file, this.cropData);
29320 setThumbBoxSize : function()
29324 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29325 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29326 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29328 this.minWidth = width;
29329 this.minHeight = height;
29331 if(this.rotate == 90 || this.rotate == 270){
29332 this.minWidth = height;
29333 this.minHeight = width;
29338 width = Math.ceil(this.minWidth * height / this.minHeight);
29340 if(this.minWidth > this.minHeight){
29342 height = Math.ceil(this.minHeight * width / this.minWidth);
29345 this.thumbEl.setStyle({
29346 width : width + 'px',
29347 height : height + 'px'
29354 setThumbBoxPosition : function()
29356 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29357 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29359 this.thumbEl.setLeft(x);
29360 this.thumbEl.setTop(y);
29364 baseRotateLevel : function()
29366 this.baseRotate = 1;
29369 typeof(this.exif) != 'undefined' &&
29370 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29371 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29373 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29376 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29380 baseScaleLevel : function()
29384 if(this.isDocument){
29386 if(this.baseRotate == 6 || this.baseRotate == 8){
29388 height = this.thumbEl.getHeight();
29389 this.baseScale = height / this.imageEl.OriginWidth;
29391 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29392 width = this.thumbEl.getWidth();
29393 this.baseScale = width / this.imageEl.OriginHeight;
29399 height = this.thumbEl.getHeight();
29400 this.baseScale = height / this.imageEl.OriginHeight;
29402 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29403 width = this.thumbEl.getWidth();
29404 this.baseScale = width / this.imageEl.OriginWidth;
29410 if(this.baseRotate == 6 || this.baseRotate == 8){
29412 width = this.thumbEl.getHeight();
29413 this.baseScale = width / this.imageEl.OriginHeight;
29415 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29416 height = this.thumbEl.getWidth();
29417 this.baseScale = height / this.imageEl.OriginHeight;
29420 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29421 height = this.thumbEl.getWidth();
29422 this.baseScale = height / this.imageEl.OriginHeight;
29424 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29425 width = this.thumbEl.getHeight();
29426 this.baseScale = width / this.imageEl.OriginWidth;
29433 width = this.thumbEl.getWidth();
29434 this.baseScale = width / this.imageEl.OriginWidth;
29436 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29437 height = this.thumbEl.getHeight();
29438 this.baseScale = height / this.imageEl.OriginHeight;
29441 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29443 height = this.thumbEl.getHeight();
29444 this.baseScale = height / this.imageEl.OriginHeight;
29446 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29447 width = this.thumbEl.getWidth();
29448 this.baseScale = width / this.imageEl.OriginWidth;
29456 getScaleLevel : function()
29458 return this.baseScale * Math.pow(1.1, this.scale);
29461 onTouchStart : function(e)
29463 if(!this.canvasLoaded){
29464 this.beforeSelectFile(e);
29468 var touches = e.browserEvent.touches;
29474 if(touches.length == 1){
29475 this.onMouseDown(e);
29479 if(touches.length != 2){
29485 for(var i = 0, finger; finger = touches[i]; i++){
29486 coords.push(finger.pageX, finger.pageY);
29489 var x = Math.pow(coords[0] - coords[2], 2);
29490 var y = Math.pow(coords[1] - coords[3], 2);
29492 this.startDistance = Math.sqrt(x + y);
29494 this.startScale = this.scale;
29496 this.pinching = true;
29497 this.dragable = false;
29501 onTouchMove : function(e)
29503 if(!this.pinching && !this.dragable){
29507 var touches = e.browserEvent.touches;
29514 this.onMouseMove(e);
29520 for(var i = 0, finger; finger = touches[i]; i++){
29521 coords.push(finger.pageX, finger.pageY);
29524 var x = Math.pow(coords[0] - coords[2], 2);
29525 var y = Math.pow(coords[1] - coords[3], 2);
29527 this.endDistance = Math.sqrt(x + y);
29529 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29531 if(!this.zoomable()){
29532 this.scale = this.startScale;
29540 onTouchEnd : function(e)
29542 this.pinching = false;
29543 this.dragable = false;
29547 process : function(file, crop)
29550 this.maskEl.mask(this.loadingText);
29553 this.xhr = new XMLHttpRequest();
29555 file.xhr = this.xhr;
29557 this.xhr.open(this.method, this.url, true);
29560 "Accept": "application/json",
29561 "Cache-Control": "no-cache",
29562 "X-Requested-With": "XMLHttpRequest"
29565 for (var headerName in headers) {
29566 var headerValue = headers[headerName];
29568 this.xhr.setRequestHeader(headerName, headerValue);
29574 this.xhr.onload = function()
29576 _this.xhrOnLoad(_this.xhr);
29579 this.xhr.onerror = function()
29581 _this.xhrOnError(_this.xhr);
29584 var formData = new FormData();
29586 formData.append('returnHTML', 'NO');
29589 formData.append('crop', crop);
29592 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29593 formData.append(this.paramName, file, file.name);
29596 if(typeof(file.filename) != 'undefined'){
29597 formData.append('filename', file.filename);
29600 if(typeof(file.mimetype) != 'undefined'){
29601 formData.append('mimetype', file.mimetype);
29604 if(this.fireEvent('arrange', this, formData) != false){
29605 this.xhr.send(formData);
29609 xhrOnLoad : function(xhr)
29612 this.maskEl.unmask();
29615 if (xhr.readyState !== 4) {
29616 this.fireEvent('exception', this, xhr);
29620 var response = Roo.decode(xhr.responseText);
29622 if(!response.success){
29623 this.fireEvent('exception', this, xhr);
29627 var response = Roo.decode(xhr.responseText);
29629 this.fireEvent('upload', this, response);
29633 xhrOnError : function()
29636 this.maskEl.unmask();
29639 Roo.log('xhr on error');
29641 var response = Roo.decode(xhr.responseText);
29647 prepare : function(file)
29650 this.maskEl.mask(this.loadingText);
29656 if(typeof(file) === 'string'){
29657 this.loadCanvas(file);
29661 if(!file || !this.urlAPI){
29666 this.cropType = file.type;
29670 if(this.fireEvent('prepare', this, this.file) != false){
29672 var reader = new FileReader();
29674 reader.onload = function (e) {
29675 if (e.target.error) {
29676 Roo.log(e.target.error);
29680 var buffer = e.target.result,
29681 dataView = new DataView(buffer),
29683 maxOffset = dataView.byteLength - 4,
29687 if (dataView.getUint16(0) === 0xffd8) {
29688 while (offset < maxOffset) {
29689 markerBytes = dataView.getUint16(offset);
29691 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29692 markerLength = dataView.getUint16(offset + 2) + 2;
29693 if (offset + markerLength > dataView.byteLength) {
29694 Roo.log('Invalid meta data: Invalid segment size.');
29698 if(markerBytes == 0xffe1){
29699 _this.parseExifData(
29706 offset += markerLength;
29716 var url = _this.urlAPI.createObjectURL(_this.file);
29718 _this.loadCanvas(url);
29723 reader.readAsArrayBuffer(this.file);
29729 parseExifData : function(dataView, offset, length)
29731 var tiffOffset = offset + 10,
29735 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29736 // No Exif data, might be XMP data instead
29740 // Check for the ASCII code for "Exif" (0x45786966):
29741 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29742 // No Exif data, might be XMP data instead
29745 if (tiffOffset + 8 > dataView.byteLength) {
29746 Roo.log('Invalid Exif data: Invalid segment size.');
29749 // Check for the two null bytes:
29750 if (dataView.getUint16(offset + 8) !== 0x0000) {
29751 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29754 // Check the byte alignment:
29755 switch (dataView.getUint16(tiffOffset)) {
29757 littleEndian = true;
29760 littleEndian = false;
29763 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29766 // Check for the TIFF tag marker (0x002A):
29767 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29768 Roo.log('Invalid Exif data: Missing TIFF marker.');
29771 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29772 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29774 this.parseExifTags(
29777 tiffOffset + dirOffset,
29782 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29787 if (dirOffset + 6 > dataView.byteLength) {
29788 Roo.log('Invalid Exif data: Invalid directory offset.');
29791 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29792 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29793 if (dirEndOffset + 4 > dataView.byteLength) {
29794 Roo.log('Invalid Exif data: Invalid directory size.');
29797 for (i = 0; i < tagsNumber; i += 1) {
29801 dirOffset + 2 + 12 * i, // tag offset
29805 // Return the offset to the next directory:
29806 return dataView.getUint32(dirEndOffset, littleEndian);
29809 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29811 var tag = dataView.getUint16(offset, littleEndian);
29813 this.exif[tag] = this.getExifValue(
29817 dataView.getUint16(offset + 2, littleEndian), // tag type
29818 dataView.getUint32(offset + 4, littleEndian), // tag length
29823 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29825 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29834 Roo.log('Invalid Exif data: Invalid tag type.');
29838 tagSize = tagType.size * length;
29839 // Determine if the value is contained in the dataOffset bytes,
29840 // or if the value at the dataOffset is a pointer to the actual data:
29841 dataOffset = tagSize > 4 ?
29842 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29843 if (dataOffset + tagSize > dataView.byteLength) {
29844 Roo.log('Invalid Exif data: Invalid data offset.');
29847 if (length === 1) {
29848 return tagType.getValue(dataView, dataOffset, littleEndian);
29851 for (i = 0; i < length; i += 1) {
29852 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29855 if (tagType.ascii) {
29857 // Concatenate the chars:
29858 for (i = 0; i < values.length; i += 1) {
29860 // Ignore the terminating NULL byte(s):
29861 if (c === '\u0000') {
29873 Roo.apply(Roo.bootstrap.UploadCropbox, {
29875 'Orientation': 0x0112
29879 1: 0, //'top-left',
29881 3: 180, //'bottom-right',
29882 // 4: 'bottom-left',
29884 6: 90, //'right-top',
29885 // 7: 'right-bottom',
29886 8: 270 //'left-bottom'
29890 // byte, 8-bit unsigned int:
29892 getValue: function (dataView, dataOffset) {
29893 return dataView.getUint8(dataOffset);
29897 // ascii, 8-bit byte:
29899 getValue: function (dataView, dataOffset) {
29900 return String.fromCharCode(dataView.getUint8(dataOffset));
29905 // short, 16 bit int:
29907 getValue: function (dataView, dataOffset, littleEndian) {
29908 return dataView.getUint16(dataOffset, littleEndian);
29912 // long, 32 bit int:
29914 getValue: function (dataView, dataOffset, littleEndian) {
29915 return dataView.getUint32(dataOffset, littleEndian);
29919 // rational = two long values, first is numerator, second is denominator:
29921 getValue: function (dataView, dataOffset, littleEndian) {
29922 return dataView.getUint32(dataOffset, littleEndian) /
29923 dataView.getUint32(dataOffset + 4, littleEndian);
29927 // slong, 32 bit signed int:
29929 getValue: function (dataView, dataOffset, littleEndian) {
29930 return dataView.getInt32(dataOffset, littleEndian);
29934 // srational, two slongs, first is numerator, second is denominator:
29936 getValue: function (dataView, dataOffset, littleEndian) {
29937 return dataView.getInt32(dataOffset, littleEndian) /
29938 dataView.getInt32(dataOffset + 4, littleEndian);
29948 cls : 'btn-group roo-upload-cropbox-rotate-left',
29949 action : 'rotate-left',
29953 cls : 'btn btn-default',
29954 html : '<i class="fa fa-undo"></i>'
29960 cls : 'btn-group roo-upload-cropbox-picture',
29961 action : 'picture',
29965 cls : 'btn btn-default',
29966 html : '<i class="fa fa-picture-o"></i>'
29972 cls : 'btn-group roo-upload-cropbox-rotate-right',
29973 action : 'rotate-right',
29977 cls : 'btn btn-default',
29978 html : '<i class="fa fa-repeat"></i>'
29986 cls : 'btn-group roo-upload-cropbox-rotate-left',
29987 action : 'rotate-left',
29991 cls : 'btn btn-default',
29992 html : '<i class="fa fa-undo"></i>'
29998 cls : 'btn-group roo-upload-cropbox-download',
29999 action : 'download',
30003 cls : 'btn btn-default',
30004 html : '<i class="fa fa-download"></i>'
30010 cls : 'btn-group roo-upload-cropbox-crop',
30015 cls : 'btn btn-default',
30016 html : '<i class="fa fa-crop"></i>'
30022 cls : 'btn-group roo-upload-cropbox-trash',
30027 cls : 'btn btn-default',
30028 html : '<i class="fa fa-trash"></i>'
30034 cls : 'btn-group roo-upload-cropbox-rotate-right',
30035 action : 'rotate-right',
30039 cls : 'btn btn-default',
30040 html : '<i class="fa fa-repeat"></i>'
30048 cls : 'btn-group roo-upload-cropbox-rotate-left',
30049 action : 'rotate-left',
30053 cls : 'btn btn-default',
30054 html : '<i class="fa fa-undo"></i>'
30060 cls : 'btn-group roo-upload-cropbox-rotate-right',
30061 action : 'rotate-right',
30065 cls : 'btn btn-default',
30066 html : '<i class="fa fa-repeat"></i>'
30079 * @class Roo.bootstrap.DocumentManager
30080 * @extends Roo.bootstrap.Component
30081 * Bootstrap DocumentManager class
30082 * @cfg {String} paramName default 'imageUpload'
30083 * @cfg {String} toolTipName default 'filename'
30084 * @cfg {String} method default POST
30085 * @cfg {String} url action url
30086 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30087 * @cfg {Boolean} multiple multiple upload default true
30088 * @cfg {Number} thumbSize default 300
30089 * @cfg {String} fieldLabel
30090 * @cfg {Number} labelWidth default 4
30091 * @cfg {String} labelAlign (left|top) default left
30092 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30093 * @cfg {Number} labellg set the width of label (1-12)
30094 * @cfg {Number} labelmd set the width of label (1-12)
30095 * @cfg {Number} labelsm set the width of label (1-12)
30096 * @cfg {Number} labelxs set the width of label (1-12)
30099 * Create a new DocumentManager
30100 * @param {Object} config The config object
30103 Roo.bootstrap.DocumentManager = function(config){
30104 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30107 this.delegates = [];
30112 * Fire when initial the DocumentManager
30113 * @param {Roo.bootstrap.DocumentManager} this
30118 * inspect selected file
30119 * @param {Roo.bootstrap.DocumentManager} this
30120 * @param {File} file
30125 * Fire when xhr load exception
30126 * @param {Roo.bootstrap.DocumentManager} this
30127 * @param {XMLHttpRequest} xhr
30129 "exception" : true,
30131 * @event afterupload
30132 * Fire when xhr load exception
30133 * @param {Roo.bootstrap.DocumentManager} this
30134 * @param {XMLHttpRequest} xhr
30136 "afterupload" : true,
30139 * prepare the form data
30140 * @param {Roo.bootstrap.DocumentManager} this
30141 * @param {Object} formData
30146 * Fire when remove the file
30147 * @param {Roo.bootstrap.DocumentManager} this
30148 * @param {Object} file
30153 * Fire after refresh the file
30154 * @param {Roo.bootstrap.DocumentManager} this
30159 * Fire after click the image
30160 * @param {Roo.bootstrap.DocumentManager} this
30161 * @param {Object} file
30166 * Fire when upload a image and editable set to true
30167 * @param {Roo.bootstrap.DocumentManager} this
30168 * @param {Object} file
30172 * @event beforeselectfile
30173 * Fire before select file
30174 * @param {Roo.bootstrap.DocumentManager} this
30176 "beforeselectfile" : true,
30179 * Fire before process file
30180 * @param {Roo.bootstrap.DocumentManager} this
30181 * @param {Object} file
30185 * @event previewrendered
30186 * Fire when preview rendered
30187 * @param {Roo.bootstrap.DocumentManager} this
30188 * @param {Object} file
30190 "previewrendered" : true,
30193 "previewResize" : true
30198 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30207 paramName : 'imageUpload',
30208 toolTipName : 'filename',
30211 labelAlign : 'left',
30221 getAutoCreate : function()
30223 var managerWidget = {
30225 cls : 'roo-document-manager',
30229 cls : 'roo-document-manager-selector',
30234 cls : 'roo-document-manager-uploader',
30238 cls : 'roo-document-manager-upload-btn',
30239 html : '<i class="fa fa-plus"></i>'
30250 cls : 'column col-md-12',
30255 if(this.fieldLabel.length){
30260 cls : 'column col-md-12',
30261 html : this.fieldLabel
30265 cls : 'column col-md-12',
30270 if(this.labelAlign == 'left'){
30275 html : this.fieldLabel
30284 if(this.labelWidth > 12){
30285 content[0].style = "width: " + this.labelWidth + 'px';
30288 if(this.labelWidth < 13 && this.labelmd == 0){
30289 this.labelmd = this.labelWidth;
30292 if(this.labellg > 0){
30293 content[0].cls += ' col-lg-' + this.labellg;
30294 content[1].cls += ' col-lg-' + (12 - this.labellg);
30297 if(this.labelmd > 0){
30298 content[0].cls += ' col-md-' + this.labelmd;
30299 content[1].cls += ' col-md-' + (12 - this.labelmd);
30302 if(this.labelsm > 0){
30303 content[0].cls += ' col-sm-' + this.labelsm;
30304 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30307 if(this.labelxs > 0){
30308 content[0].cls += ' col-xs-' + this.labelxs;
30309 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30317 cls : 'row clearfix',
30325 initEvents : function()
30327 this.managerEl = this.el.select('.roo-document-manager', true).first();
30328 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30330 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30331 this.selectorEl.hide();
30334 this.selectorEl.attr('multiple', 'multiple');
30337 this.selectorEl.on('change', this.onFileSelected, this);
30339 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30340 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30342 this.uploader.on('click', this.onUploaderClick, this);
30344 this.renderProgressDialog();
30348 window.addEventListener("resize", function() { _this.refresh(); } );
30350 this.fireEvent('initial', this);
30353 renderProgressDialog : function()
30357 this.progressDialog = new Roo.bootstrap.Modal({
30358 cls : 'roo-document-manager-progress-dialog',
30359 allow_close : false,
30370 btnclick : function() {
30371 _this.uploadCancel();
30377 this.progressDialog.render(Roo.get(document.body));
30379 this.progress = new Roo.bootstrap.Progress({
30380 cls : 'roo-document-manager-progress',
30385 this.progress.render(this.progressDialog.getChildContainer());
30387 this.progressBar = new Roo.bootstrap.ProgressBar({
30388 cls : 'roo-document-manager-progress-bar',
30391 aria_valuemax : 12,
30395 this.progressBar.render(this.progress.getChildContainer());
30398 onUploaderClick : function(e)
30400 e.preventDefault();
30402 if(this.fireEvent('beforeselectfile', this) != false){
30403 this.selectorEl.dom.click();
30408 onFileSelected : function(e)
30410 e.preventDefault();
30412 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30416 Roo.each(this.selectorEl.dom.files, function(file){
30417 if(this.fireEvent('inspect', this, file) != false){
30418 this.files.push(file);
30428 this.selectorEl.dom.value = '';
30430 if(!this.files || !this.files.length){
30434 if(this.boxes > 0 && this.files.length > this.boxes){
30435 this.files = this.files.slice(0, this.boxes);
30438 this.uploader.show();
30440 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30441 this.uploader.hide();
30450 Roo.each(this.files, function(file){
30452 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30453 var f = this.renderPreview(file);
30458 if(file.type.indexOf('image') != -1){
30459 this.delegates.push(
30461 _this.process(file);
30462 }).createDelegate(this)
30470 _this.process(file);
30471 }).createDelegate(this)
30476 this.files = files;
30478 this.delegates = this.delegates.concat(docs);
30480 if(!this.delegates.length){
30485 this.progressBar.aria_valuemax = this.delegates.length;
30492 arrange : function()
30494 if(!this.delegates.length){
30495 this.progressDialog.hide();
30500 var delegate = this.delegates.shift();
30502 this.progressDialog.show();
30504 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30506 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30511 refresh : function()
30513 this.uploader.show();
30515 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30516 this.uploader.hide();
30519 Roo.isTouch ? this.closable(false) : this.closable(true);
30521 this.fireEvent('refresh', this);
30524 onRemove : function(e, el, o)
30526 e.preventDefault();
30528 this.fireEvent('remove', this, o);
30532 remove : function(o)
30536 Roo.each(this.files, function(file){
30537 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30546 this.files = files;
30553 Roo.each(this.files, function(file){
30558 file.target.remove();
30567 onClick : function(e, el, o)
30569 e.preventDefault();
30571 this.fireEvent('click', this, o);
30575 closable : function(closable)
30577 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30579 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30591 xhrOnLoad : function(xhr)
30593 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30597 if (xhr.readyState !== 4) {
30599 this.fireEvent('exception', this, xhr);
30603 var response = Roo.decode(xhr.responseText);
30605 if(!response.success){
30607 this.fireEvent('exception', this, xhr);
30611 var file = this.renderPreview(response.data);
30613 this.files.push(file);
30617 this.fireEvent('afterupload', this, xhr);
30621 xhrOnError : function(xhr)
30623 Roo.log('xhr on error');
30625 var response = Roo.decode(xhr.responseText);
30632 process : function(file)
30634 if(this.fireEvent('process', this, file) !== false){
30635 if(this.editable && file.type.indexOf('image') != -1){
30636 this.fireEvent('edit', this, file);
30640 this.uploadStart(file, false);
30647 uploadStart : function(file, crop)
30649 this.xhr = new XMLHttpRequest();
30651 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30656 file.xhr = this.xhr;
30658 this.managerEl.createChild({
30660 cls : 'roo-document-manager-loading',
30664 tooltip : file.name,
30665 cls : 'roo-document-manager-thumb',
30666 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30672 this.xhr.open(this.method, this.url, true);
30675 "Accept": "application/json",
30676 "Cache-Control": "no-cache",
30677 "X-Requested-With": "XMLHttpRequest"
30680 for (var headerName in headers) {
30681 var headerValue = headers[headerName];
30683 this.xhr.setRequestHeader(headerName, headerValue);
30689 this.xhr.onload = function()
30691 _this.xhrOnLoad(_this.xhr);
30694 this.xhr.onerror = function()
30696 _this.xhrOnError(_this.xhr);
30699 var formData = new FormData();
30701 formData.append('returnHTML', 'NO');
30704 formData.append('crop', crop);
30707 formData.append(this.paramName, file, file.name);
30714 if(this.fireEvent('prepare', this, formData, options) != false){
30716 if(options.manually){
30720 this.xhr.send(formData);
30724 this.uploadCancel();
30727 uploadCancel : function()
30733 this.delegates = [];
30735 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30742 renderPreview : function(file)
30744 if(typeof(file.target) != 'undefined' && file.target){
30748 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30750 var previewEl = this.managerEl.createChild({
30752 cls : 'roo-document-manager-preview',
30756 tooltip : file[this.toolTipName],
30757 cls : 'roo-document-manager-thumb',
30758 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30763 html : '<i class="fa fa-times-circle"></i>'
30768 var close = previewEl.select('button.close', true).first();
30770 close.on('click', this.onRemove, this, file);
30772 file.target = previewEl;
30774 var image = previewEl.select('img', true).first();
30778 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30780 image.on('click', this.onClick, this, file);
30782 this.fireEvent('previewrendered', this, file);
30788 onPreviewLoad : function(file, image)
30790 if(typeof(file.target) == 'undefined' || !file.target){
30794 var width = image.dom.naturalWidth || image.dom.width;
30795 var height = image.dom.naturalHeight || image.dom.height;
30797 if(!this.previewResize) {
30801 if(width > height){
30802 file.target.addClass('wide');
30806 file.target.addClass('tall');
30811 uploadFromSource : function(file, crop)
30813 this.xhr = new XMLHttpRequest();
30815 this.managerEl.createChild({
30817 cls : 'roo-document-manager-loading',
30821 tooltip : file.name,
30822 cls : 'roo-document-manager-thumb',
30823 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30829 this.xhr.open(this.method, this.url, true);
30832 "Accept": "application/json",
30833 "Cache-Control": "no-cache",
30834 "X-Requested-With": "XMLHttpRequest"
30837 for (var headerName in headers) {
30838 var headerValue = headers[headerName];
30840 this.xhr.setRequestHeader(headerName, headerValue);
30846 this.xhr.onload = function()
30848 _this.xhrOnLoad(_this.xhr);
30851 this.xhr.onerror = function()
30853 _this.xhrOnError(_this.xhr);
30856 var formData = new FormData();
30858 formData.append('returnHTML', 'NO');
30860 formData.append('crop', crop);
30862 if(typeof(file.filename) != 'undefined'){
30863 formData.append('filename', file.filename);
30866 if(typeof(file.mimetype) != 'undefined'){
30867 formData.append('mimetype', file.mimetype);
30872 if(this.fireEvent('prepare', this, formData) != false){
30873 this.xhr.send(formData);
30883 * @class Roo.bootstrap.DocumentViewer
30884 * @extends Roo.bootstrap.Component
30885 * Bootstrap DocumentViewer class
30886 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30887 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30890 * Create a new DocumentViewer
30891 * @param {Object} config The config object
30894 Roo.bootstrap.DocumentViewer = function(config){
30895 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30900 * Fire after initEvent
30901 * @param {Roo.bootstrap.DocumentViewer} this
30907 * @param {Roo.bootstrap.DocumentViewer} this
30912 * Fire after download button
30913 * @param {Roo.bootstrap.DocumentViewer} this
30918 * Fire after trash button
30919 * @param {Roo.bootstrap.DocumentViewer} this
30926 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30928 showDownload : true,
30932 getAutoCreate : function()
30936 cls : 'roo-document-viewer',
30940 cls : 'roo-document-viewer-body',
30944 cls : 'roo-document-viewer-thumb',
30948 cls : 'roo-document-viewer-image'
30956 cls : 'roo-document-viewer-footer',
30959 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30963 cls : 'btn-group roo-document-viewer-download',
30967 cls : 'btn btn-default',
30968 html : '<i class="fa fa-download"></i>'
30974 cls : 'btn-group roo-document-viewer-trash',
30978 cls : 'btn btn-default',
30979 html : '<i class="fa fa-trash"></i>'
30992 initEvents : function()
30994 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30995 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30997 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30998 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31000 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31001 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31003 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31004 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31006 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31007 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31009 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31010 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31012 this.bodyEl.on('click', this.onClick, this);
31013 this.downloadBtn.on('click', this.onDownload, this);
31014 this.trashBtn.on('click', this.onTrash, this);
31016 this.downloadBtn.hide();
31017 this.trashBtn.hide();
31019 if(this.showDownload){
31020 this.downloadBtn.show();
31023 if(this.showTrash){
31024 this.trashBtn.show();
31027 if(!this.showDownload && !this.showTrash) {
31028 this.footerEl.hide();
31033 initial : function()
31035 this.fireEvent('initial', this);
31039 onClick : function(e)
31041 e.preventDefault();
31043 this.fireEvent('click', this);
31046 onDownload : function(e)
31048 e.preventDefault();
31050 this.fireEvent('download', this);
31053 onTrash : function(e)
31055 e.preventDefault();
31057 this.fireEvent('trash', this);
31069 * @class Roo.bootstrap.NavProgressBar
31070 * @extends Roo.bootstrap.Component
31071 * Bootstrap NavProgressBar class
31074 * Create a new nav progress bar
31075 * @param {Object} config The config object
31078 Roo.bootstrap.NavProgressBar = function(config){
31079 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31081 this.bullets = this.bullets || [];
31083 // Roo.bootstrap.NavProgressBar.register(this);
31087 * Fires when the active item changes
31088 * @param {Roo.bootstrap.NavProgressBar} this
31089 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31090 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31097 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31102 getAutoCreate : function()
31104 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31108 cls : 'roo-navigation-bar-group',
31112 cls : 'roo-navigation-top-bar'
31116 cls : 'roo-navigation-bullets-bar',
31120 cls : 'roo-navigation-bar'
31127 cls : 'roo-navigation-bottom-bar'
31137 initEvents: function()
31142 onRender : function(ct, position)
31144 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31146 if(this.bullets.length){
31147 Roo.each(this.bullets, function(b){
31156 addItem : function(cfg)
31158 var item = new Roo.bootstrap.NavProgressItem(cfg);
31160 item.parentId = this.id;
31161 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31164 var top = new Roo.bootstrap.Element({
31166 cls : 'roo-navigation-bar-text'
31169 var bottom = new Roo.bootstrap.Element({
31171 cls : 'roo-navigation-bar-text'
31174 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31175 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31177 var topText = new Roo.bootstrap.Element({
31179 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31182 var bottomText = new Roo.bootstrap.Element({
31184 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31187 topText.onRender(top.el, null);
31188 bottomText.onRender(bottom.el, null);
31191 item.bottomEl = bottom;
31194 this.barItems.push(item);
31199 getActive : function()
31201 var active = false;
31203 Roo.each(this.barItems, function(v){
31205 if (!v.isActive()) {
31217 setActiveItem : function(item)
31221 Roo.each(this.barItems, function(v){
31222 if (v.rid == item.rid) {
31226 if (v.isActive()) {
31227 v.setActive(false);
31232 item.setActive(true);
31234 this.fireEvent('changed', this, item, prev);
31237 getBarItem: function(rid)
31241 Roo.each(this.barItems, function(e) {
31242 if (e.rid != rid) {
31253 indexOfItem : function(item)
31257 Roo.each(this.barItems, function(v, i){
31259 if (v.rid != item.rid) {
31270 setActiveNext : function()
31272 var i = this.indexOfItem(this.getActive());
31274 if (i > this.barItems.length) {
31278 this.setActiveItem(this.barItems[i+1]);
31281 setActivePrev : function()
31283 var i = this.indexOfItem(this.getActive());
31289 this.setActiveItem(this.barItems[i-1]);
31292 format : function()
31294 if(!this.barItems.length){
31298 var width = 100 / this.barItems.length;
31300 Roo.each(this.barItems, function(i){
31301 i.el.setStyle('width', width + '%');
31302 i.topEl.el.setStyle('width', width + '%');
31303 i.bottomEl.el.setStyle('width', width + '%');
31312 * Nav Progress Item
31317 * @class Roo.bootstrap.NavProgressItem
31318 * @extends Roo.bootstrap.Component
31319 * Bootstrap NavProgressItem class
31320 * @cfg {String} rid the reference id
31321 * @cfg {Boolean} active (true|false) Is item active default false
31322 * @cfg {Boolean} disabled (true|false) Is item active default false
31323 * @cfg {String} html
31324 * @cfg {String} position (top|bottom) text position default bottom
31325 * @cfg {String} icon show icon instead of number
31328 * Create a new NavProgressItem
31329 * @param {Object} config The config object
31331 Roo.bootstrap.NavProgressItem = function(config){
31332 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31337 * The raw click event for the entire grid.
31338 * @param {Roo.bootstrap.NavProgressItem} this
31339 * @param {Roo.EventObject} e
31346 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
31352 position : 'bottom',
31355 getAutoCreate : function()
31357 var iconCls = 'roo-navigation-bar-item-icon';
31359 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31363 cls: 'roo-navigation-bar-item',
31373 cfg.cls += ' active';
31376 cfg.cls += ' disabled';
31382 disable : function()
31384 this.setDisabled(true);
31387 enable : function()
31389 this.setDisabled(false);
31392 initEvents: function()
31394 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31396 this.iconEl.on('click', this.onClick, this);
31399 onClick : function(e)
31401 e.preventDefault();
31407 if(this.fireEvent('click', this, e) === false){
31411 this.parent().setActiveItem(this);
31414 isActive: function ()
31416 return this.active;
31419 setActive : function(state)
31421 if(this.active == state){
31425 this.active = state;
31428 this.el.addClass('active');
31432 this.el.removeClass('active');
31437 setDisabled : function(state)
31439 if(this.disabled == state){
31443 this.disabled = state;
31446 this.el.addClass('disabled');
31450 this.el.removeClass('disabled');
31453 tooltipEl : function()
31455 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31468 * @class Roo.bootstrap.FieldLabel
31469 * @extends Roo.bootstrap.Component
31470 * Bootstrap FieldLabel class
31471 * @cfg {String} html contents of the element
31472 * @cfg {String} tag tag of the element default label
31473 * @cfg {String} cls class of the element
31474 * @cfg {String} target label target
31475 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31476 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31477 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31478 * @cfg {String} iconTooltip default "This field is required"
31479 * @cfg {String} indicatorpos (left|right) default left
31482 * Create a new FieldLabel
31483 * @param {Object} config The config object
31486 Roo.bootstrap.FieldLabel = function(config){
31487 Roo.bootstrap.Element.superclass.constructor.call(this, config);
31492 * Fires after the field has been marked as invalid.
31493 * @param {Roo.form.FieldLabel} this
31494 * @param {String} msg The validation message
31499 * Fires after the field has been validated with no errors.
31500 * @param {Roo.form.FieldLabel} this
31506 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
31513 invalidClass : 'has-warning',
31514 validClass : 'has-success',
31515 iconTooltip : 'This field is required',
31516 indicatorpos : 'left',
31518 getAutoCreate : function(){
31521 if (!this.allowBlank) {
31527 cls : 'roo-bootstrap-field-label ' + this.cls,
31532 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31533 tooltip : this.iconTooltip
31542 if(this.indicatorpos == 'right'){
31545 cls : 'roo-bootstrap-field-label ' + this.cls,
31554 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31555 tooltip : this.iconTooltip
31564 initEvents: function()
31566 Roo.bootstrap.Element.superclass.initEvents.call(this);
31568 this.indicator = this.indicatorEl();
31570 if(this.indicator){
31571 this.indicator.removeClass('visible');
31572 this.indicator.addClass('invisible');
31575 Roo.bootstrap.FieldLabel.register(this);
31578 indicatorEl : function()
31580 var indicator = this.el.select('i.roo-required-indicator',true).first();
31591 * Mark this field as valid
31593 markValid : function()
31595 if(this.indicator){
31596 this.indicator.removeClass('visible');
31597 this.indicator.addClass('invisible');
31599 if (Roo.bootstrap.version == 3) {
31600 this.el.removeClass(this.invalidClass);
31601 this.el.addClass(this.validClass);
31603 this.el.removeClass('is-invalid');
31604 this.el.addClass('is-valid');
31608 this.fireEvent('valid', this);
31612 * Mark this field as invalid
31613 * @param {String} msg The validation message
31615 markInvalid : function(msg)
31617 if(this.indicator){
31618 this.indicator.removeClass('invisible');
31619 this.indicator.addClass('visible');
31621 if (Roo.bootstrap.version == 3) {
31622 this.el.removeClass(this.validClass);
31623 this.el.addClass(this.invalidClass);
31625 this.el.removeClass('is-valid');
31626 this.el.addClass('is-invalid');
31630 this.fireEvent('invalid', this, msg);
31636 Roo.apply(Roo.bootstrap.FieldLabel, {
31641 * register a FieldLabel Group
31642 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31644 register : function(label)
31646 if(this.groups.hasOwnProperty(label.target)){
31650 this.groups[label.target] = label;
31654 * fetch a FieldLabel Group based on the target
31655 * @param {string} target
31656 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31658 get: function(target) {
31659 if (typeof(this.groups[target]) == 'undefined') {
31663 return this.groups[target] ;
31672 * page DateSplitField.
31678 * @class Roo.bootstrap.DateSplitField
31679 * @extends Roo.bootstrap.Component
31680 * Bootstrap DateSplitField class
31681 * @cfg {string} fieldLabel - the label associated
31682 * @cfg {Number} labelWidth set the width of label (0-12)
31683 * @cfg {String} labelAlign (top|left)
31684 * @cfg {Boolean} dayAllowBlank (true|false) default false
31685 * @cfg {Boolean} monthAllowBlank (true|false) default false
31686 * @cfg {Boolean} yearAllowBlank (true|false) default false
31687 * @cfg {string} dayPlaceholder
31688 * @cfg {string} monthPlaceholder
31689 * @cfg {string} yearPlaceholder
31690 * @cfg {string} dayFormat default 'd'
31691 * @cfg {string} monthFormat default 'm'
31692 * @cfg {string} yearFormat default 'Y'
31693 * @cfg {Number} labellg set the width of label (1-12)
31694 * @cfg {Number} labelmd set the width of label (1-12)
31695 * @cfg {Number} labelsm set the width of label (1-12)
31696 * @cfg {Number} labelxs set the width of label (1-12)
31700 * Create a new DateSplitField
31701 * @param {Object} config The config object
31704 Roo.bootstrap.DateSplitField = function(config){
31705 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31711 * getting the data of years
31712 * @param {Roo.bootstrap.DateSplitField} this
31713 * @param {Object} years
31718 * getting the data of days
31719 * @param {Roo.bootstrap.DateSplitField} this
31720 * @param {Object} days
31725 * Fires after the field has been marked as invalid.
31726 * @param {Roo.form.Field} this
31727 * @param {String} msg The validation message
31732 * Fires after the field has been validated with no errors.
31733 * @param {Roo.form.Field} this
31739 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31742 labelAlign : 'top',
31744 dayAllowBlank : false,
31745 monthAllowBlank : false,
31746 yearAllowBlank : false,
31747 dayPlaceholder : '',
31748 monthPlaceholder : '',
31749 yearPlaceholder : '',
31753 isFormField : true,
31759 getAutoCreate : function()
31763 cls : 'row roo-date-split-field-group',
31768 cls : 'form-hidden-field roo-date-split-field-group-value',
31774 var labelCls = 'col-md-12';
31775 var contentCls = 'col-md-4';
31777 if(this.fieldLabel){
31781 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31785 html : this.fieldLabel
31790 if(this.labelAlign == 'left'){
31792 if(this.labelWidth > 12){
31793 label.style = "width: " + this.labelWidth + 'px';
31796 if(this.labelWidth < 13 && this.labelmd == 0){
31797 this.labelmd = this.labelWidth;
31800 if(this.labellg > 0){
31801 labelCls = ' col-lg-' + this.labellg;
31802 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31805 if(this.labelmd > 0){
31806 labelCls = ' col-md-' + this.labelmd;
31807 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31810 if(this.labelsm > 0){
31811 labelCls = ' col-sm-' + this.labelsm;
31812 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31815 if(this.labelxs > 0){
31816 labelCls = ' col-xs-' + this.labelxs;
31817 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31821 label.cls += ' ' + labelCls;
31823 cfg.cn.push(label);
31826 Roo.each(['day', 'month', 'year'], function(t){
31829 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31836 inputEl: function ()
31838 return this.el.select('.roo-date-split-field-group-value', true).first();
31841 onRender : function(ct, position)
31845 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31847 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31849 this.dayField = new Roo.bootstrap.ComboBox({
31850 allowBlank : this.dayAllowBlank,
31851 alwaysQuery : true,
31852 displayField : 'value',
31855 forceSelection : true,
31857 placeholder : this.dayPlaceholder,
31858 selectOnFocus : true,
31859 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31860 triggerAction : 'all',
31862 valueField : 'value',
31863 store : new Roo.data.SimpleStore({
31864 data : (function() {
31866 _this.fireEvent('days', _this, days);
31869 fields : [ 'value' ]
31872 select : function (_self, record, index)
31874 _this.setValue(_this.getValue());
31879 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31881 this.monthField = new Roo.bootstrap.MonthField({
31882 after : '<i class=\"fa fa-calendar\"></i>',
31883 allowBlank : this.monthAllowBlank,
31884 placeholder : this.monthPlaceholder,
31887 render : function (_self)
31889 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31890 e.preventDefault();
31894 select : function (_self, oldvalue, newvalue)
31896 _this.setValue(_this.getValue());
31901 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31903 this.yearField = new Roo.bootstrap.ComboBox({
31904 allowBlank : this.yearAllowBlank,
31905 alwaysQuery : true,
31906 displayField : 'value',
31909 forceSelection : true,
31911 placeholder : this.yearPlaceholder,
31912 selectOnFocus : true,
31913 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31914 triggerAction : 'all',
31916 valueField : 'value',
31917 store : new Roo.data.SimpleStore({
31918 data : (function() {
31920 _this.fireEvent('years', _this, years);
31923 fields : [ 'value' ]
31926 select : function (_self, record, index)
31928 _this.setValue(_this.getValue());
31933 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31936 setValue : function(v, format)
31938 this.inputEl.dom.value = v;
31940 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31942 var d = Date.parseDate(v, f);
31949 this.setDay(d.format(this.dayFormat));
31950 this.setMonth(d.format(this.monthFormat));
31951 this.setYear(d.format(this.yearFormat));
31958 setDay : function(v)
31960 this.dayField.setValue(v);
31961 this.inputEl.dom.value = this.getValue();
31966 setMonth : function(v)
31968 this.monthField.setValue(v, true);
31969 this.inputEl.dom.value = this.getValue();
31974 setYear : function(v)
31976 this.yearField.setValue(v);
31977 this.inputEl.dom.value = this.getValue();
31982 getDay : function()
31984 return this.dayField.getValue();
31987 getMonth : function()
31989 return this.monthField.getValue();
31992 getYear : function()
31994 return this.yearField.getValue();
31997 getValue : function()
31999 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32001 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32011 this.inputEl.dom.value = '';
32016 validate : function()
32018 var d = this.dayField.validate();
32019 var m = this.monthField.validate();
32020 var y = this.yearField.validate();
32025 (!this.dayAllowBlank && !d) ||
32026 (!this.monthAllowBlank && !m) ||
32027 (!this.yearAllowBlank && !y)
32032 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32041 this.markInvalid();
32046 markValid : function()
32049 var label = this.el.select('label', true).first();
32050 var icon = this.el.select('i.fa-star', true).first();
32056 this.fireEvent('valid', this);
32060 * Mark this field as invalid
32061 * @param {String} msg The validation message
32063 markInvalid : function(msg)
32066 var label = this.el.select('label', true).first();
32067 var icon = this.el.select('i.fa-star', true).first();
32069 if(label && !icon){
32070 this.el.select('.roo-date-split-field-label', true).createChild({
32072 cls : 'text-danger fa fa-lg fa-star',
32073 tooltip : 'This field is required',
32074 style : 'margin-right:5px;'
32078 this.fireEvent('invalid', this, msg);
32081 clearInvalid : function()
32083 var label = this.el.select('label', true).first();
32084 var icon = this.el.select('i.fa-star', true).first();
32090 this.fireEvent('valid', this);
32093 getName: function()
32103 * http://masonry.desandro.com
32105 * The idea is to render all the bricks based on vertical width...
32107 * The original code extends 'outlayer' - we might need to use that....
32113 * @class Roo.bootstrap.LayoutMasonry
32114 * @extends Roo.bootstrap.Component
32115 * Bootstrap Layout Masonry class
32118 * Create a new Element
32119 * @param {Object} config The config object
32122 Roo.bootstrap.LayoutMasonry = function(config){
32124 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32128 Roo.bootstrap.LayoutMasonry.register(this);
32134 * Fire after layout the items
32135 * @param {Roo.bootstrap.LayoutMasonry} this
32136 * @param {Roo.EventObject} e
32143 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32146 * @cfg {Boolean} isLayoutInstant = no animation?
32148 isLayoutInstant : false, // needed?
32151 * @cfg {Number} boxWidth width of the columns
32156 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32161 * @cfg {Number} padWidth padding below box..
32166 * @cfg {Number} gutter gutter width..
32171 * @cfg {Number} maxCols maximum number of columns
32177 * @cfg {Boolean} isAutoInitial defalut true
32179 isAutoInitial : true,
32184 * @cfg {Boolean} isHorizontal defalut false
32186 isHorizontal : false,
32188 currentSize : null,
32194 bricks: null, //CompositeElement
32198 _isLayoutInited : false,
32200 // isAlternative : false, // only use for vertical layout...
32203 * @cfg {Number} alternativePadWidth padding below box..
32205 alternativePadWidth : 50,
32207 selectedBrick : [],
32209 getAutoCreate : function(){
32211 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32215 cls: 'blog-masonary-wrapper ' + this.cls,
32217 cls : 'mas-boxes masonary'
32224 getChildContainer: function( )
32226 if (this.boxesEl) {
32227 return this.boxesEl;
32230 this.boxesEl = this.el.select('.mas-boxes').first();
32232 return this.boxesEl;
32236 initEvents : function()
32240 if(this.isAutoInitial){
32241 Roo.log('hook children rendered');
32242 this.on('childrenrendered', function() {
32243 Roo.log('children rendered');
32249 initial : function()
32251 this.selectedBrick = [];
32253 this.currentSize = this.el.getBox(true);
32255 Roo.EventManager.onWindowResize(this.resize, this);
32257 if(!this.isAutoInitial){
32265 //this.layout.defer(500,this);
32269 resize : function()
32271 var cs = this.el.getBox(true);
32274 this.currentSize.width == cs.width &&
32275 this.currentSize.x == cs.x &&
32276 this.currentSize.height == cs.height &&
32277 this.currentSize.y == cs.y
32279 Roo.log("no change in with or X or Y");
32283 this.currentSize = cs;
32289 layout : function()
32291 this._resetLayout();
32293 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32295 this.layoutItems( isInstant );
32297 this._isLayoutInited = true;
32299 this.fireEvent('layout', this);
32303 _resetLayout : function()
32305 if(this.isHorizontal){
32306 this.horizontalMeasureColumns();
32310 this.verticalMeasureColumns();
32314 verticalMeasureColumns : function()
32316 this.getContainerWidth();
32318 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32319 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32323 var boxWidth = this.boxWidth + this.padWidth;
32325 if(this.containerWidth < this.boxWidth){
32326 boxWidth = this.containerWidth
32329 var containerWidth = this.containerWidth;
32331 var cols = Math.floor(containerWidth / boxWidth);
32333 this.cols = Math.max( cols, 1 );
32335 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32337 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32339 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32341 this.colWidth = boxWidth + avail - this.padWidth;
32343 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32344 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32347 horizontalMeasureColumns : function()
32349 this.getContainerWidth();
32351 var boxWidth = this.boxWidth;
32353 if(this.containerWidth < boxWidth){
32354 boxWidth = this.containerWidth;
32357 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32359 this.el.setHeight(boxWidth);
32363 getContainerWidth : function()
32365 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32368 layoutItems : function( isInstant )
32370 Roo.log(this.bricks);
32372 var items = Roo.apply([], this.bricks);
32374 if(this.isHorizontal){
32375 this._horizontalLayoutItems( items , isInstant );
32379 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32380 // this._verticalAlternativeLayoutItems( items , isInstant );
32384 this._verticalLayoutItems( items , isInstant );
32388 _verticalLayoutItems : function ( items , isInstant)
32390 if ( !items || !items.length ) {
32395 ['xs', 'xs', 'xs', 'tall'],
32396 ['xs', 'xs', 'tall'],
32397 ['xs', 'xs', 'sm'],
32398 ['xs', 'xs', 'xs'],
32404 ['sm', 'xs', 'xs'],
32408 ['tall', 'xs', 'xs', 'xs'],
32409 ['tall', 'xs', 'xs'],
32421 Roo.each(items, function(item, k){
32423 switch (item.size) {
32424 // these layouts take up a full box,
32435 boxes.push([item]);
32458 var filterPattern = function(box, length)
32466 var pattern = box.slice(0, length);
32470 Roo.each(pattern, function(i){
32471 format.push(i.size);
32474 Roo.each(standard, function(s){
32476 if(String(s) != String(format)){
32485 if(!match && length == 1){
32490 filterPattern(box, length - 1);
32494 queue.push(pattern);
32496 box = box.slice(length, box.length);
32498 filterPattern(box, 4);
32504 Roo.each(boxes, function(box, k){
32510 if(box.length == 1){
32515 filterPattern(box, 4);
32519 this._processVerticalLayoutQueue( queue, isInstant );
32523 // _verticalAlternativeLayoutItems : function( items , isInstant )
32525 // if ( !items || !items.length ) {
32529 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
32533 _horizontalLayoutItems : function ( items , isInstant)
32535 if ( !items || !items.length || items.length < 3) {
32541 var eItems = items.slice(0, 3);
32543 items = items.slice(3, items.length);
32546 ['xs', 'xs', 'xs', 'wide'],
32547 ['xs', 'xs', 'wide'],
32548 ['xs', 'xs', 'sm'],
32549 ['xs', 'xs', 'xs'],
32555 ['sm', 'xs', 'xs'],
32559 ['wide', 'xs', 'xs', 'xs'],
32560 ['wide', 'xs', 'xs'],
32573 Roo.each(items, function(item, k){
32575 switch (item.size) {
32586 boxes.push([item]);
32610 var filterPattern = function(box, length)
32618 var pattern = box.slice(0, length);
32622 Roo.each(pattern, function(i){
32623 format.push(i.size);
32626 Roo.each(standard, function(s){
32628 if(String(s) != String(format)){
32637 if(!match && length == 1){
32642 filterPattern(box, length - 1);
32646 queue.push(pattern);
32648 box = box.slice(length, box.length);
32650 filterPattern(box, 4);
32656 Roo.each(boxes, function(box, k){
32662 if(box.length == 1){
32667 filterPattern(box, 4);
32674 var pos = this.el.getBox(true);
32678 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32680 var hit_end = false;
32682 Roo.each(queue, function(box){
32686 Roo.each(box, function(b){
32688 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32698 Roo.each(box, function(b){
32700 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32703 mx = Math.max(mx, b.x);
32707 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32711 Roo.each(box, function(b){
32713 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32727 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32730 /** Sets position of item in DOM
32731 * @param {Element} item
32732 * @param {Number} x - horizontal position
32733 * @param {Number} y - vertical position
32734 * @param {Boolean} isInstant - disables transitions
32736 _processVerticalLayoutQueue : function( queue, isInstant )
32738 var pos = this.el.getBox(true);
32743 for (var i = 0; i < this.cols; i++){
32747 Roo.each(queue, function(box, k){
32749 var col = k % this.cols;
32751 Roo.each(box, function(b,kk){
32753 b.el.position('absolute');
32755 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32756 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32758 if(b.size == 'md-left' || b.size == 'md-right'){
32759 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32760 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32763 b.el.setWidth(width);
32764 b.el.setHeight(height);
32766 b.el.select('iframe',true).setSize(width,height);
32770 for (var i = 0; i < this.cols; i++){
32772 if(maxY[i] < maxY[col]){
32777 col = Math.min(col, i);
32781 x = pos.x + col * (this.colWidth + this.padWidth);
32785 var positions = [];
32787 switch (box.length){
32789 positions = this.getVerticalOneBoxColPositions(x, y, box);
32792 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32795 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32798 positions = this.getVerticalFourBoxColPositions(x, y, box);
32804 Roo.each(box, function(b,kk){
32806 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32808 var sz = b.el.getSize();
32810 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32818 for (var i = 0; i < this.cols; i++){
32819 mY = Math.max(mY, maxY[i]);
32822 this.el.setHeight(mY - pos.y);
32826 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32828 // var pos = this.el.getBox(true);
32831 // var maxX = pos.right;
32833 // var maxHeight = 0;
32835 // Roo.each(items, function(item, k){
32839 // item.el.position('absolute');
32841 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32843 // item.el.setWidth(width);
32845 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32847 // item.el.setHeight(height);
32850 // item.el.setXY([x, y], isInstant ? false : true);
32852 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32855 // y = y + height + this.alternativePadWidth;
32857 // maxHeight = maxHeight + height + this.alternativePadWidth;
32861 // this.el.setHeight(maxHeight);
32865 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32867 var pos = this.el.getBox(true);
32872 var maxX = pos.right;
32874 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32876 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32878 Roo.each(queue, function(box, k){
32880 Roo.each(box, function(b, kk){
32882 b.el.position('absolute');
32884 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32885 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32887 if(b.size == 'md-left' || b.size == 'md-right'){
32888 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32889 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32892 b.el.setWidth(width);
32893 b.el.setHeight(height);
32901 var positions = [];
32903 switch (box.length){
32905 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32908 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32911 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32914 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32920 Roo.each(box, function(b,kk){
32922 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32924 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32932 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32934 Roo.each(eItems, function(b,k){
32936 b.size = (k == 0) ? 'sm' : 'xs';
32937 b.x = (k == 0) ? 2 : 1;
32938 b.y = (k == 0) ? 2 : 1;
32940 b.el.position('absolute');
32942 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32944 b.el.setWidth(width);
32946 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32948 b.el.setHeight(height);
32952 var positions = [];
32955 x : maxX - this.unitWidth * 2 - this.gutter,
32960 x : maxX - this.unitWidth,
32961 y : minY + (this.unitWidth + this.gutter) * 2
32965 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32969 Roo.each(eItems, function(b,k){
32971 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32977 getVerticalOneBoxColPositions : function(x, y, box)
32981 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32983 if(box[0].size == 'md-left'){
32987 if(box[0].size == 'md-right'){
32992 x : x + (this.unitWidth + this.gutter) * rand,
32999 getVerticalTwoBoxColPositions : function(x, y, box)
33003 if(box[0].size == 'xs'){
33007 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33011 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33025 x : x + (this.unitWidth + this.gutter) * 2,
33026 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33033 getVerticalThreeBoxColPositions : function(x, y, box)
33037 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33045 x : x + (this.unitWidth + this.gutter) * 1,
33050 x : x + (this.unitWidth + this.gutter) * 2,
33058 if(box[0].size == 'xs' && box[1].size == 'xs'){
33067 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33071 x : x + (this.unitWidth + this.gutter) * 1,
33085 x : x + (this.unitWidth + this.gutter) * 2,
33090 x : x + (this.unitWidth + this.gutter) * 2,
33091 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33098 getVerticalFourBoxColPositions : function(x, y, box)
33102 if(box[0].size == 'xs'){
33111 y : y + (this.unitHeight + this.gutter) * 1
33116 y : y + (this.unitHeight + this.gutter) * 2
33120 x : x + (this.unitWidth + this.gutter) * 1,
33134 x : x + (this.unitWidth + this.gutter) * 2,
33139 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33140 y : y + (this.unitHeight + this.gutter) * 1
33144 x : x + (this.unitWidth + this.gutter) * 2,
33145 y : y + (this.unitWidth + this.gutter) * 2
33152 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33156 if(box[0].size == 'md-left'){
33158 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33165 if(box[0].size == 'md-right'){
33167 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33168 y : minY + (this.unitWidth + this.gutter) * 1
33174 var rand = Math.floor(Math.random() * (4 - box[0].y));
33177 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33178 y : minY + (this.unitWidth + this.gutter) * rand
33185 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33189 if(box[0].size == 'xs'){
33192 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33197 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33198 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33206 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33211 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33212 y : minY + (this.unitWidth + this.gutter) * 2
33219 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33223 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33226 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33231 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33232 y : minY + (this.unitWidth + this.gutter) * 1
33236 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33237 y : minY + (this.unitWidth + this.gutter) * 2
33244 if(box[0].size == 'xs' && box[1].size == 'xs'){
33247 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33252 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33257 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33258 y : minY + (this.unitWidth + this.gutter) * 1
33266 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33271 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33272 y : minY + (this.unitWidth + this.gutter) * 2
33276 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33277 y : minY + (this.unitWidth + this.gutter) * 2
33284 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33288 if(box[0].size == 'xs'){
33291 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33296 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33301 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),
33306 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33307 y : minY + (this.unitWidth + this.gutter) * 1
33315 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33320 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33321 y : minY + (this.unitWidth + this.gutter) * 2
33325 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33326 y : minY + (this.unitWidth + this.gutter) * 2
33330 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),
33331 y : minY + (this.unitWidth + this.gutter) * 2
33339 * remove a Masonry Brick
33340 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33342 removeBrick : function(brick_id)
33348 for (var i = 0; i<this.bricks.length; i++) {
33349 if (this.bricks[i].id == brick_id) {
33350 this.bricks.splice(i,1);
33351 this.el.dom.removeChild(Roo.get(brick_id).dom);
33358 * adds a Masonry Brick
33359 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33361 addBrick : function(cfg)
33363 var cn = new Roo.bootstrap.MasonryBrick(cfg);
33364 //this.register(cn);
33365 cn.parentId = this.id;
33366 cn.render(this.el);
33371 * register a Masonry Brick
33372 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33375 register : function(brick)
33377 this.bricks.push(brick);
33378 brick.masonryId = this.id;
33382 * clear all the Masonry Brick
33384 clearAll : function()
33387 //this.getChildContainer().dom.innerHTML = "";
33388 this.el.dom.innerHTML = '';
33391 getSelected : function()
33393 if (!this.selectedBrick) {
33397 return this.selectedBrick;
33401 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33405 * register a Masonry Layout
33406 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33409 register : function(layout)
33411 this.groups[layout.id] = layout;
33414 * fetch a Masonry Layout based on the masonry layout ID
33415 * @param {string} the masonry layout to add
33416 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33419 get: function(layout_id) {
33420 if (typeof(this.groups[layout_id]) == 'undefined') {
33423 return this.groups[layout_id] ;
33435 * http://masonry.desandro.com
33437 * The idea is to render all the bricks based on vertical width...
33439 * The original code extends 'outlayer' - we might need to use that....
33445 * @class Roo.bootstrap.LayoutMasonryAuto
33446 * @extends Roo.bootstrap.Component
33447 * Bootstrap Layout Masonry class
33450 * Create a new Element
33451 * @param {Object} config The config object
33454 Roo.bootstrap.LayoutMasonryAuto = function(config){
33455 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33458 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
33461 * @cfg {Boolean} isFitWidth - resize the width..
33463 isFitWidth : false, // options..
33465 * @cfg {Boolean} isOriginLeft = left align?
33467 isOriginLeft : true,
33469 * @cfg {Boolean} isOriginTop = top align?
33471 isOriginTop : false,
33473 * @cfg {Boolean} isLayoutInstant = no animation?
33475 isLayoutInstant : false, // needed?
33477 * @cfg {Boolean} isResizingContainer = not sure if this is used..
33479 isResizingContainer : true,
33481 * @cfg {Number} columnWidth width of the columns
33487 * @cfg {Number} maxCols maximum number of columns
33492 * @cfg {Number} padHeight padding below box..
33498 * @cfg {Boolean} isAutoInitial defalut true
33501 isAutoInitial : true,
33507 initialColumnWidth : 0,
33508 currentSize : null,
33510 colYs : null, // array.
33517 bricks: null, //CompositeElement
33518 cols : 0, // array?
33519 // element : null, // wrapped now this.el
33520 _isLayoutInited : null,
33523 getAutoCreate : function(){
33527 cls: 'blog-masonary-wrapper ' + this.cls,
33529 cls : 'mas-boxes masonary'
33536 getChildContainer: function( )
33538 if (this.boxesEl) {
33539 return this.boxesEl;
33542 this.boxesEl = this.el.select('.mas-boxes').first();
33544 return this.boxesEl;
33548 initEvents : function()
33552 if(this.isAutoInitial){
33553 Roo.log('hook children rendered');
33554 this.on('childrenrendered', function() {
33555 Roo.log('children rendered');
33562 initial : function()
33564 this.reloadItems();
33566 this.currentSize = this.el.getBox(true);
33568 /// was window resize... - let's see if this works..
33569 Roo.EventManager.onWindowResize(this.resize, this);
33571 if(!this.isAutoInitial){
33576 this.layout.defer(500,this);
33579 reloadItems: function()
33581 this.bricks = this.el.select('.masonry-brick', true);
33583 this.bricks.each(function(b) {
33584 //Roo.log(b.getSize());
33585 if (!b.attr('originalwidth')) {
33586 b.attr('originalwidth', b.getSize().width);
33591 Roo.log(this.bricks.elements.length);
33594 resize : function()
33597 var cs = this.el.getBox(true);
33599 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33600 Roo.log("no change in with or X");
33603 this.currentSize = cs;
33607 layout : function()
33610 this._resetLayout();
33611 //this._manageStamps();
33613 // don't animate first layout
33614 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33615 this.layoutItems( isInstant );
33617 // flag for initalized
33618 this._isLayoutInited = true;
33621 layoutItems : function( isInstant )
33623 //var items = this._getItemsForLayout( this.items );
33624 // original code supports filtering layout items.. we just ignore it..
33626 this._layoutItems( this.bricks , isInstant );
33628 this._postLayout();
33630 _layoutItems : function ( items , isInstant)
33632 //this.fireEvent( 'layout', this, items );
33635 if ( !items || !items.elements.length ) {
33636 // no items, emit event with empty array
33641 items.each(function(item) {
33642 Roo.log("layout item");
33644 // get x/y object from method
33645 var position = this._getItemLayoutPosition( item );
33647 position.item = item;
33648 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33649 queue.push( position );
33652 this._processLayoutQueue( queue );
33654 /** Sets position of item in DOM
33655 * @param {Element} item
33656 * @param {Number} x - horizontal position
33657 * @param {Number} y - vertical position
33658 * @param {Boolean} isInstant - disables transitions
33660 _processLayoutQueue : function( queue )
33662 for ( var i=0, len = queue.length; i < len; i++ ) {
33663 var obj = queue[i];
33664 obj.item.position('absolute');
33665 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33671 * Any logic you want to do after each layout,
33672 * i.e. size the container
33674 _postLayout : function()
33676 this.resizeContainer();
33679 resizeContainer : function()
33681 if ( !this.isResizingContainer ) {
33684 var size = this._getContainerSize();
33686 this.el.setSize(size.width,size.height);
33687 this.boxesEl.setSize(size.width,size.height);
33693 _resetLayout : function()
33695 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33696 this.colWidth = this.el.getWidth();
33697 //this.gutter = this.el.getWidth();
33699 this.measureColumns();
33705 this.colYs.push( 0 );
33711 measureColumns : function()
33713 this.getContainerWidth();
33714 // if columnWidth is 0, default to outerWidth of first item
33715 if ( !this.columnWidth ) {
33716 var firstItem = this.bricks.first();
33717 Roo.log(firstItem);
33718 this.columnWidth = this.containerWidth;
33719 if (firstItem && firstItem.attr('originalwidth') ) {
33720 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33722 // columnWidth fall back to item of first element
33723 Roo.log("set column width?");
33724 this.initialColumnWidth = this.columnWidth ;
33726 // if first elem has no width, default to size of container
33731 if (this.initialColumnWidth) {
33732 this.columnWidth = this.initialColumnWidth;
33737 // column width is fixed at the top - however if container width get's smaller we should
33740 // this bit calcs how man columns..
33742 var columnWidth = this.columnWidth += this.gutter;
33744 // calculate columns
33745 var containerWidth = this.containerWidth + this.gutter;
33747 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33748 // fix rounding errors, typically with gutters
33749 var excess = columnWidth - containerWidth % columnWidth;
33752 // if overshoot is less than a pixel, round up, otherwise floor it
33753 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33754 cols = Math[ mathMethod ]( cols );
33755 this.cols = Math.max( cols, 1 );
33756 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33758 // padding positioning..
33759 var totalColWidth = this.cols * this.columnWidth;
33760 var padavail = this.containerWidth - totalColWidth;
33761 // so for 2 columns - we need 3 'pads'
33763 var padNeeded = (1+this.cols) * this.padWidth;
33765 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33767 this.columnWidth += padExtra
33768 //this.padWidth = Math.floor(padavail / ( this.cols));
33770 // adjust colum width so that padding is fixed??
33772 // we have 3 columns ... total = width * 3
33773 // we have X left over... that should be used by
33775 //if (this.expandC) {
33783 getContainerWidth : function()
33785 /* // container is parent if fit width
33786 var container = this.isFitWidth ? this.element.parentNode : this.element;
33787 // check that this.size and size are there
33788 // IE8 triggers resize on body size change, so they might not be
33790 var size = getSize( container ); //FIXME
33791 this.containerWidth = size && size.innerWidth; //FIXME
33794 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33798 _getItemLayoutPosition : function( item ) // what is item?
33800 // we resize the item to our columnWidth..
33802 item.setWidth(this.columnWidth);
33803 item.autoBoxAdjust = false;
33805 var sz = item.getSize();
33807 // how many columns does this brick span
33808 var remainder = this.containerWidth % this.columnWidth;
33810 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33811 // round if off by 1 pixel, otherwise use ceil
33812 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33813 colSpan = Math.min( colSpan, this.cols );
33815 // normally this should be '1' as we dont' currently allow multi width columns..
33817 var colGroup = this._getColGroup( colSpan );
33818 // get the minimum Y value from the columns
33819 var minimumY = Math.min.apply( Math, colGroup );
33820 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33822 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33824 // position the brick
33826 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33827 y: this.currentSize.y + minimumY + this.padHeight
33831 // apply setHeight to necessary columns
33832 var setHeight = minimumY + sz.height + this.padHeight;
33833 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33835 var setSpan = this.cols + 1 - colGroup.length;
33836 for ( var i = 0; i < setSpan; i++ ) {
33837 this.colYs[ shortColIndex + i ] = setHeight ;
33844 * @param {Number} colSpan - number of columns the element spans
33845 * @returns {Array} colGroup
33847 _getColGroup : function( colSpan )
33849 if ( colSpan < 2 ) {
33850 // if brick spans only one column, use all the column Ys
33855 // how many different places could this brick fit horizontally
33856 var groupCount = this.cols + 1 - colSpan;
33857 // for each group potential horizontal position
33858 for ( var i = 0; i < groupCount; i++ ) {
33859 // make an array of colY values for that one group
33860 var groupColYs = this.colYs.slice( i, i + colSpan );
33861 // and get the max value of the array
33862 colGroup[i] = Math.max.apply( Math, groupColYs );
33867 _manageStamp : function( stamp )
33869 var stampSize = stamp.getSize();
33870 var offset = stamp.getBox();
33871 // get the columns that this stamp affects
33872 var firstX = this.isOriginLeft ? offset.x : offset.right;
33873 var lastX = firstX + stampSize.width;
33874 var firstCol = Math.floor( firstX / this.columnWidth );
33875 firstCol = Math.max( 0, firstCol );
33877 var lastCol = Math.floor( lastX / this.columnWidth );
33878 // lastCol should not go over if multiple of columnWidth #425
33879 lastCol -= lastX % this.columnWidth ? 0 : 1;
33880 lastCol = Math.min( this.cols - 1, lastCol );
33882 // set colYs to bottom of the stamp
33883 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33886 for ( var i = firstCol; i <= lastCol; i++ ) {
33887 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33892 _getContainerSize : function()
33894 this.maxY = Math.max.apply( Math, this.colYs );
33899 if ( this.isFitWidth ) {
33900 size.width = this._getContainerFitWidth();
33906 _getContainerFitWidth : function()
33908 var unusedCols = 0;
33909 // count unused columns
33912 if ( this.colYs[i] !== 0 ) {
33917 // fit container to columns that have been used
33918 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33921 needsResizeLayout : function()
33923 var previousWidth = this.containerWidth;
33924 this.getContainerWidth();
33925 return previousWidth !== this.containerWidth;
33940 * @class Roo.bootstrap.MasonryBrick
33941 * @extends Roo.bootstrap.Component
33942 * Bootstrap MasonryBrick class
33945 * Create a new MasonryBrick
33946 * @param {Object} config The config object
33949 Roo.bootstrap.MasonryBrick = function(config){
33951 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33953 Roo.bootstrap.MasonryBrick.register(this);
33959 * When a MasonryBrick is clcik
33960 * @param {Roo.bootstrap.MasonryBrick} this
33961 * @param {Roo.EventObject} e
33967 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33970 * @cfg {String} title
33974 * @cfg {String} html
33978 * @cfg {String} bgimage
33982 * @cfg {String} videourl
33986 * @cfg {String} cls
33990 * @cfg {String} href
33994 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33999 * @cfg {String} placetitle (center|bottom)
34004 * @cfg {Boolean} isFitContainer defalut true
34006 isFitContainer : true,
34009 * @cfg {Boolean} preventDefault defalut false
34011 preventDefault : false,
34014 * @cfg {Boolean} inverse defalut false
34016 maskInverse : false,
34018 getAutoCreate : function()
34020 if(!this.isFitContainer){
34021 return this.getSplitAutoCreate();
34024 var cls = 'masonry-brick masonry-brick-full';
34026 if(this.href.length){
34027 cls += ' masonry-brick-link';
34030 if(this.bgimage.length){
34031 cls += ' masonry-brick-image';
34034 if(this.maskInverse){
34035 cls += ' mask-inverse';
34038 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34039 cls += ' enable-mask';
34043 cls += ' masonry-' + this.size + '-brick';
34046 if(this.placetitle.length){
34048 switch (this.placetitle) {
34050 cls += ' masonry-center-title';
34053 cls += ' masonry-bottom-title';
34060 if(!this.html.length && !this.bgimage.length){
34061 cls += ' masonry-center-title';
34064 if(!this.html.length && this.bgimage.length){
34065 cls += ' masonry-bottom-title';
34070 cls += ' ' + this.cls;
34074 tag: (this.href.length) ? 'a' : 'div',
34079 cls: 'masonry-brick-mask'
34083 cls: 'masonry-brick-paragraph',
34089 if(this.href.length){
34090 cfg.href = this.href;
34093 var cn = cfg.cn[1].cn;
34095 if(this.title.length){
34098 cls: 'masonry-brick-title',
34103 if(this.html.length){
34106 cls: 'masonry-brick-text',
34111 if (!this.title.length && !this.html.length) {
34112 cfg.cn[1].cls += ' hide';
34115 if(this.bgimage.length){
34118 cls: 'masonry-brick-image-view',
34123 if(this.videourl.length){
34124 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34125 // youtube support only?
34128 cls: 'masonry-brick-image-view',
34131 allowfullscreen : true
34139 getSplitAutoCreate : function()
34141 var cls = 'masonry-brick masonry-brick-split';
34143 if(this.href.length){
34144 cls += ' masonry-brick-link';
34147 if(this.bgimage.length){
34148 cls += ' masonry-brick-image';
34152 cls += ' masonry-' + this.size + '-brick';
34155 switch (this.placetitle) {
34157 cls += ' masonry-center-title';
34160 cls += ' masonry-bottom-title';
34163 if(!this.bgimage.length){
34164 cls += ' masonry-center-title';
34167 if(this.bgimage.length){
34168 cls += ' masonry-bottom-title';
34174 cls += ' ' + this.cls;
34178 tag: (this.href.length) ? 'a' : 'div',
34183 cls: 'masonry-brick-split-head',
34187 cls: 'masonry-brick-paragraph',
34194 cls: 'masonry-brick-split-body',
34200 if(this.href.length){
34201 cfg.href = this.href;
34204 if(this.title.length){
34205 cfg.cn[0].cn[0].cn.push({
34207 cls: 'masonry-brick-title',
34212 if(this.html.length){
34213 cfg.cn[1].cn.push({
34215 cls: 'masonry-brick-text',
34220 if(this.bgimage.length){
34221 cfg.cn[0].cn.push({
34223 cls: 'masonry-brick-image-view',
34228 if(this.videourl.length){
34229 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34230 // youtube support only?
34231 cfg.cn[0].cn.cn.push({
34233 cls: 'masonry-brick-image-view',
34236 allowfullscreen : true
34243 initEvents: function()
34245 switch (this.size) {
34278 this.el.on('touchstart', this.onTouchStart, this);
34279 this.el.on('touchmove', this.onTouchMove, this);
34280 this.el.on('touchend', this.onTouchEnd, this);
34281 this.el.on('contextmenu', this.onContextMenu, this);
34283 this.el.on('mouseenter' ,this.enter, this);
34284 this.el.on('mouseleave', this.leave, this);
34285 this.el.on('click', this.onClick, this);
34288 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34289 this.parent().bricks.push(this);
34294 onClick: function(e, el)
34296 var time = this.endTimer - this.startTimer;
34297 // Roo.log(e.preventDefault());
34300 e.preventDefault();
34305 if(!this.preventDefault){
34309 e.preventDefault();
34311 if (this.activeClass != '') {
34312 this.selectBrick();
34315 this.fireEvent('click', this, e);
34318 enter: function(e, el)
34320 e.preventDefault();
34322 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34326 if(this.bgimage.length && this.html.length){
34327 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34331 leave: function(e, el)
34333 e.preventDefault();
34335 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34339 if(this.bgimage.length && this.html.length){
34340 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34344 onTouchStart: function(e, el)
34346 // e.preventDefault();
34348 this.touchmoved = false;
34350 if(!this.isFitContainer){
34354 if(!this.bgimage.length || !this.html.length){
34358 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34360 this.timer = new Date().getTime();
34364 onTouchMove: function(e, el)
34366 this.touchmoved = true;
34369 onContextMenu : function(e,el)
34371 e.preventDefault();
34372 e.stopPropagation();
34376 onTouchEnd: function(e, el)
34378 // e.preventDefault();
34380 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34387 if(!this.bgimage.length || !this.html.length){
34389 if(this.href.length){
34390 window.location.href = this.href;
34396 if(!this.isFitContainer){
34400 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34402 window.location.href = this.href;
34405 //selection on single brick only
34406 selectBrick : function() {
34408 if (!this.parentId) {
34412 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34413 var index = m.selectedBrick.indexOf(this.id);
34416 m.selectedBrick.splice(index,1);
34417 this.el.removeClass(this.activeClass);
34421 for(var i = 0; i < m.selectedBrick.length; i++) {
34422 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34423 b.el.removeClass(b.activeClass);
34426 m.selectedBrick = [];
34428 m.selectedBrick.push(this.id);
34429 this.el.addClass(this.activeClass);
34433 isSelected : function(){
34434 return this.el.hasClass(this.activeClass);
34439 Roo.apply(Roo.bootstrap.MasonryBrick, {
34442 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34444 * register a Masonry Brick
34445 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34448 register : function(brick)
34450 //this.groups[brick.id] = brick;
34451 this.groups.add(brick.id, brick);
34454 * fetch a masonry brick based on the masonry brick ID
34455 * @param {string} the masonry brick to add
34456 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34459 get: function(brick_id)
34461 // if (typeof(this.groups[brick_id]) == 'undefined') {
34464 // return this.groups[brick_id] ;
34466 if(this.groups.key(brick_id)) {
34467 return this.groups.key(brick_id);
34485 * @class Roo.bootstrap.Brick
34486 * @extends Roo.bootstrap.Component
34487 * Bootstrap Brick class
34490 * Create a new Brick
34491 * @param {Object} config The config object
34494 Roo.bootstrap.Brick = function(config){
34495 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34501 * When a Brick is click
34502 * @param {Roo.bootstrap.Brick} this
34503 * @param {Roo.EventObject} e
34509 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
34512 * @cfg {String} title
34516 * @cfg {String} html
34520 * @cfg {String} bgimage
34524 * @cfg {String} cls
34528 * @cfg {String} href
34532 * @cfg {String} video
34536 * @cfg {Boolean} square
34540 getAutoCreate : function()
34542 var cls = 'roo-brick';
34544 if(this.href.length){
34545 cls += ' roo-brick-link';
34548 if(this.bgimage.length){
34549 cls += ' roo-brick-image';
34552 if(!this.html.length && !this.bgimage.length){
34553 cls += ' roo-brick-center-title';
34556 if(!this.html.length && this.bgimage.length){
34557 cls += ' roo-brick-bottom-title';
34561 cls += ' ' + this.cls;
34565 tag: (this.href.length) ? 'a' : 'div',
34570 cls: 'roo-brick-paragraph',
34576 if(this.href.length){
34577 cfg.href = this.href;
34580 var cn = cfg.cn[0].cn;
34582 if(this.title.length){
34585 cls: 'roo-brick-title',
34590 if(this.html.length){
34593 cls: 'roo-brick-text',
34600 if(this.bgimage.length){
34603 cls: 'roo-brick-image-view',
34611 initEvents: function()
34613 if(this.title.length || this.html.length){
34614 this.el.on('mouseenter' ,this.enter, this);
34615 this.el.on('mouseleave', this.leave, this);
34618 Roo.EventManager.onWindowResize(this.resize, this);
34620 if(this.bgimage.length){
34621 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34622 this.imageEl.on('load', this.onImageLoad, this);
34629 onImageLoad : function()
34634 resize : function()
34636 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34638 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34640 if(this.bgimage.length){
34641 var image = this.el.select('.roo-brick-image-view', true).first();
34643 image.setWidth(paragraph.getWidth());
34646 image.setHeight(paragraph.getWidth());
34649 this.el.setHeight(image.getHeight());
34650 paragraph.setHeight(image.getHeight());
34656 enter: function(e, el)
34658 e.preventDefault();
34660 if(this.bgimage.length){
34661 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34662 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34666 leave: function(e, el)
34668 e.preventDefault();
34670 if(this.bgimage.length){
34671 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34672 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34687 * @class Roo.bootstrap.NumberField
34688 * @extends Roo.bootstrap.Input
34689 * Bootstrap NumberField class
34695 * Create a new NumberField
34696 * @param {Object} config The config object
34699 Roo.bootstrap.NumberField = function(config){
34700 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34703 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34706 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34708 allowDecimals : true,
34710 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34712 decimalSeparator : ".",
34714 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34716 decimalPrecision : 2,
34718 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34720 allowNegative : true,
34723 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34727 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34729 minValue : Number.NEGATIVE_INFINITY,
34731 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34733 maxValue : Number.MAX_VALUE,
34735 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34737 minText : "The minimum value for this field is {0}",
34739 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34741 maxText : "The maximum value for this field is {0}",
34743 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34744 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34746 nanText : "{0} is not a valid number",
34748 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34750 thousandsDelimiter : false,
34752 * @cfg {String} valueAlign alignment of value
34754 valueAlign : "left",
34756 getAutoCreate : function()
34758 var hiddenInput = {
34762 cls: 'hidden-number-input'
34766 hiddenInput.name = this.name;
34771 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34773 this.name = hiddenInput.name;
34775 if(cfg.cn.length > 0) {
34776 cfg.cn.push(hiddenInput);
34783 initEvents : function()
34785 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34787 var allowed = "0123456789";
34789 if(this.allowDecimals){
34790 allowed += this.decimalSeparator;
34793 if(this.allowNegative){
34797 if(this.thousandsDelimiter) {
34801 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34803 var keyPress = function(e){
34805 var k = e.getKey();
34807 var c = e.getCharCode();
34810 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34811 allowed.indexOf(String.fromCharCode(c)) === -1
34817 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34821 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34826 this.el.on("keypress", keyPress, this);
34829 validateValue : function(value)
34832 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34836 var num = this.parseValue(value);
34839 this.markInvalid(String.format(this.nanText, value));
34843 if(num < this.minValue){
34844 this.markInvalid(String.format(this.minText, this.minValue));
34848 if(num > this.maxValue){
34849 this.markInvalid(String.format(this.maxText, this.maxValue));
34856 getValue : function()
34858 var v = this.hiddenEl().getValue();
34860 return this.fixPrecision(this.parseValue(v));
34863 parseValue : function(value)
34865 if(this.thousandsDelimiter) {
34867 r = new RegExp(",", "g");
34868 value = value.replace(r, "");
34871 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34872 return isNaN(value) ? '' : value;
34875 fixPrecision : function(value)
34877 if(this.thousandsDelimiter) {
34879 r = new RegExp(",", "g");
34880 value = value.replace(r, "");
34883 var nan = isNaN(value);
34885 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34886 return nan ? '' : value;
34888 return parseFloat(value).toFixed(this.decimalPrecision);
34891 setValue : function(v)
34893 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34899 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34901 this.inputEl().dom.value = (v == '') ? '' :
34902 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34904 if(!this.allowZero && v === '0') {
34905 this.hiddenEl().dom.value = '';
34906 this.inputEl().dom.value = '';
34913 decimalPrecisionFcn : function(v)
34915 return Math.floor(v);
34918 beforeBlur : function()
34920 var v = this.parseValue(this.getRawValue());
34922 if(v || v === 0 || v === ''){
34927 hiddenEl : function()
34929 return this.el.select('input.hidden-number-input',true).first();
34941 * @class Roo.bootstrap.DocumentSlider
34942 * @extends Roo.bootstrap.Component
34943 * Bootstrap DocumentSlider class
34946 * Create a new DocumentViewer
34947 * @param {Object} config The config object
34950 Roo.bootstrap.DocumentSlider = function(config){
34951 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34958 * Fire after initEvent
34959 * @param {Roo.bootstrap.DocumentSlider} this
34964 * Fire after update
34965 * @param {Roo.bootstrap.DocumentSlider} this
34971 * @param {Roo.bootstrap.DocumentSlider} this
34977 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34983 getAutoCreate : function()
34987 cls : 'roo-document-slider',
34991 cls : 'roo-document-slider-header',
34995 cls : 'roo-document-slider-header-title'
35001 cls : 'roo-document-slider-body',
35005 cls : 'roo-document-slider-prev',
35009 cls : 'fa fa-chevron-left'
35015 cls : 'roo-document-slider-thumb',
35019 cls : 'roo-document-slider-image'
35025 cls : 'roo-document-slider-next',
35029 cls : 'fa fa-chevron-right'
35041 initEvents : function()
35043 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35044 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35046 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35047 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35049 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35050 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35052 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35053 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35055 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35056 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35058 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35059 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35061 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35062 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35064 this.thumbEl.on('click', this.onClick, this);
35066 this.prevIndicator.on('click', this.prev, this);
35068 this.nextIndicator.on('click', this.next, this);
35072 initial : function()
35074 if(this.files.length){
35075 this.indicator = 1;
35079 this.fireEvent('initial', this);
35082 update : function()
35084 this.imageEl.attr('src', this.files[this.indicator - 1]);
35086 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35088 this.prevIndicator.show();
35090 if(this.indicator == 1){
35091 this.prevIndicator.hide();
35094 this.nextIndicator.show();
35096 if(this.indicator == this.files.length){
35097 this.nextIndicator.hide();
35100 this.thumbEl.scrollTo('top');
35102 this.fireEvent('update', this);
35105 onClick : function(e)
35107 e.preventDefault();
35109 this.fireEvent('click', this);
35114 e.preventDefault();
35116 this.indicator = Math.max(1, this.indicator - 1);
35123 e.preventDefault();
35125 this.indicator = Math.min(this.files.length, this.indicator + 1);
35139 * @class Roo.bootstrap.RadioSet
35140 * @extends Roo.bootstrap.Input
35141 * Bootstrap RadioSet class
35142 * @cfg {String} indicatorpos (left|right) default left
35143 * @cfg {Boolean} inline (true|false) inline the element (default true)
35144 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35146 * Create a new RadioSet
35147 * @param {Object} config The config object
35150 Roo.bootstrap.RadioSet = function(config){
35152 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35156 Roo.bootstrap.RadioSet.register(this);
35161 * Fires when the element is checked or unchecked.
35162 * @param {Roo.bootstrap.RadioSet} this This radio
35163 * @param {Roo.bootstrap.Radio} item The checked item
35168 * Fires when the element is click.
35169 * @param {Roo.bootstrap.RadioSet} this This radio set
35170 * @param {Roo.bootstrap.Radio} item The checked item
35171 * @param {Roo.EventObject} e The event object
35178 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35186 indicatorpos : 'left',
35188 getAutoCreate : function()
35192 cls : 'roo-radio-set-label',
35196 html : this.fieldLabel
35200 if (Roo.bootstrap.version == 3) {
35203 if(this.indicatorpos == 'left'){
35206 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35207 tooltip : 'This field is required'
35212 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35213 tooltip : 'This field is required'
35219 cls : 'roo-radio-set-items'
35222 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35224 if (align === 'left' && this.fieldLabel.length) {
35227 cls : "roo-radio-set-right",
35233 if(this.labelWidth > 12){
35234 label.style = "width: " + this.labelWidth + 'px';
35237 if(this.labelWidth < 13 && this.labelmd == 0){
35238 this.labelmd = this.labelWidth;
35241 if(this.labellg > 0){
35242 label.cls += ' col-lg-' + this.labellg;
35243 items.cls += ' col-lg-' + (12 - this.labellg);
35246 if(this.labelmd > 0){
35247 label.cls += ' col-md-' + this.labelmd;
35248 items.cls += ' col-md-' + (12 - this.labelmd);
35251 if(this.labelsm > 0){
35252 label.cls += ' col-sm-' + this.labelsm;
35253 items.cls += ' col-sm-' + (12 - this.labelsm);
35256 if(this.labelxs > 0){
35257 label.cls += ' col-xs-' + this.labelxs;
35258 items.cls += ' col-xs-' + (12 - this.labelxs);
35264 cls : 'roo-radio-set',
35268 cls : 'roo-radio-set-input',
35271 value : this.value ? this.value : ''
35278 if(this.weight.length){
35279 cfg.cls += ' roo-radio-' + this.weight;
35283 cfg.cls += ' roo-radio-set-inline';
35287 ['xs','sm','md','lg'].map(function(size){
35288 if (settings[size]) {
35289 cfg.cls += ' col-' + size + '-' + settings[size];
35297 initEvents : function()
35299 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35300 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35302 if(!this.fieldLabel.length){
35303 this.labelEl.hide();
35306 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35307 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35309 this.indicator = this.indicatorEl();
35311 if(this.indicator){
35312 this.indicator.addClass('invisible');
35315 this.originalValue = this.getValue();
35319 inputEl: function ()
35321 return this.el.select('.roo-radio-set-input', true).first();
35324 getChildContainer : function()
35326 return this.itemsEl;
35329 register : function(item)
35331 this.radioes.push(item);
35335 validate : function()
35337 if(this.getVisibilityEl().hasClass('hidden')){
35343 Roo.each(this.radioes, function(i){
35352 if(this.allowBlank) {
35356 if(this.disabled || valid){
35361 this.markInvalid();
35366 markValid : function()
35368 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35369 this.indicatorEl().removeClass('visible');
35370 this.indicatorEl().addClass('invisible');
35374 if (Roo.bootstrap.version == 3) {
35375 this.el.removeClass([this.invalidClass, this.validClass]);
35376 this.el.addClass(this.validClass);
35378 this.el.removeClass(['is-invalid','is-valid']);
35379 this.el.addClass(['is-valid']);
35381 this.fireEvent('valid', this);
35384 markInvalid : function(msg)
35386 if(this.allowBlank || this.disabled){
35390 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35391 this.indicatorEl().removeClass('invisible');
35392 this.indicatorEl().addClass('visible');
35394 if (Roo.bootstrap.version == 3) {
35395 this.el.removeClass([this.invalidClass, this.validClass]);
35396 this.el.addClass(this.invalidClass);
35398 this.el.removeClass(['is-invalid','is-valid']);
35399 this.el.addClass(['is-invalid']);
35402 this.fireEvent('invalid', this, msg);
35406 setValue : function(v, suppressEvent)
35408 if(this.value === v){
35415 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35418 Roo.each(this.radioes, function(i){
35420 i.el.removeClass('checked');
35423 Roo.each(this.radioes, function(i){
35425 if(i.value === v || i.value.toString() === v.toString()){
35427 i.el.addClass('checked');
35429 if(suppressEvent !== true){
35430 this.fireEvent('check', this, i);
35441 clearInvalid : function(){
35443 if(!this.el || this.preventMark){
35447 this.el.removeClass([this.invalidClass]);
35449 this.fireEvent('valid', this);
35454 Roo.apply(Roo.bootstrap.RadioSet, {
35458 register : function(set)
35460 this.groups[set.name] = set;
35463 get: function(name)
35465 if (typeof(this.groups[name]) == 'undefined') {
35469 return this.groups[name] ;
35475 * Ext JS Library 1.1.1
35476 * Copyright(c) 2006-2007, Ext JS, LLC.
35478 * Originally Released Under LGPL - original licence link has changed is not relivant.
35481 * <script type="text/javascript">
35486 * @class Roo.bootstrap.SplitBar
35487 * @extends Roo.util.Observable
35488 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35492 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35493 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35494 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35495 split.minSize = 100;
35496 split.maxSize = 600;
35497 split.animate = true;
35498 split.on('moved', splitterMoved);
35501 * Create a new SplitBar
35502 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
35503 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
35504 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35505 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
35506 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35507 position of the SplitBar).
35509 Roo.bootstrap.SplitBar = function(cfg){
35514 // dragElement : elm
35515 // resizingElement: el,
35517 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35518 // placement : Roo.bootstrap.SplitBar.LEFT ,
35519 // existingProxy ???
35522 this.el = Roo.get(cfg.dragElement, true);
35523 this.el.dom.unselectable = "on";
35525 this.resizingEl = Roo.get(cfg.resizingElement, true);
35529 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35530 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35533 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35536 * The minimum size of the resizing element. (Defaults to 0)
35542 * The maximum size of the resizing element. (Defaults to 2000)
35545 this.maxSize = 2000;
35548 * Whether to animate the transition to the new size
35551 this.animate = false;
35554 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35557 this.useShim = false;
35562 if(!cfg.existingProxy){
35564 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35566 this.proxy = Roo.get(cfg.existingProxy).dom;
35569 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35572 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35575 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35578 this.dragSpecs = {};
35581 * @private The adapter to use to positon and resize elements
35583 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35584 this.adapter.init(this);
35586 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35588 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35589 this.el.addClass("roo-splitbar-h");
35592 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35593 this.el.addClass("roo-splitbar-v");
35599 * Fires when the splitter is moved (alias for {@link #event-moved})
35600 * @param {Roo.bootstrap.SplitBar} this
35601 * @param {Number} newSize the new width or height
35606 * Fires when the splitter is moved
35607 * @param {Roo.bootstrap.SplitBar} this
35608 * @param {Number} newSize the new width or height
35612 * @event beforeresize
35613 * Fires before the splitter is dragged
35614 * @param {Roo.bootstrap.SplitBar} this
35616 "beforeresize" : true,
35618 "beforeapply" : true
35621 Roo.util.Observable.call(this);
35624 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35625 onStartProxyDrag : function(x, y){
35626 this.fireEvent("beforeresize", this);
35628 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35630 o.enableDisplayMode("block");
35631 // all splitbars share the same overlay
35632 Roo.bootstrap.SplitBar.prototype.overlay = o;
35634 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35635 this.overlay.show();
35636 Roo.get(this.proxy).setDisplayed("block");
35637 var size = this.adapter.getElementSize(this);
35638 this.activeMinSize = this.getMinimumSize();;
35639 this.activeMaxSize = this.getMaximumSize();;
35640 var c1 = size - this.activeMinSize;
35641 var c2 = Math.max(this.activeMaxSize - size, 0);
35642 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35643 this.dd.resetConstraints();
35644 this.dd.setXConstraint(
35645 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35646 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35648 this.dd.setYConstraint(0, 0);
35650 this.dd.resetConstraints();
35651 this.dd.setXConstraint(0, 0);
35652 this.dd.setYConstraint(
35653 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35654 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35657 this.dragSpecs.startSize = size;
35658 this.dragSpecs.startPoint = [x, y];
35659 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35663 * @private Called after the drag operation by the DDProxy
35665 onEndProxyDrag : function(e){
35666 Roo.get(this.proxy).setDisplayed(false);
35667 var endPoint = Roo.lib.Event.getXY(e);
35669 this.overlay.hide();
35672 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35673 newSize = this.dragSpecs.startSize +
35674 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35675 endPoint[0] - this.dragSpecs.startPoint[0] :
35676 this.dragSpecs.startPoint[0] - endPoint[0]
35679 newSize = this.dragSpecs.startSize +
35680 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35681 endPoint[1] - this.dragSpecs.startPoint[1] :
35682 this.dragSpecs.startPoint[1] - endPoint[1]
35685 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35686 if(newSize != this.dragSpecs.startSize){
35687 if(this.fireEvent('beforeapply', this, newSize) !== false){
35688 this.adapter.setElementSize(this, newSize);
35689 this.fireEvent("moved", this, newSize);
35690 this.fireEvent("resize", this, newSize);
35696 * Get the adapter this SplitBar uses
35697 * @return The adapter object
35699 getAdapter : function(){
35700 return this.adapter;
35704 * Set the adapter this SplitBar uses
35705 * @param {Object} adapter A SplitBar adapter object
35707 setAdapter : function(adapter){
35708 this.adapter = adapter;
35709 this.adapter.init(this);
35713 * Gets the minimum size for the resizing element
35714 * @return {Number} The minimum size
35716 getMinimumSize : function(){
35717 return this.minSize;
35721 * Sets the minimum size for the resizing element
35722 * @param {Number} minSize The minimum size
35724 setMinimumSize : function(minSize){
35725 this.minSize = minSize;
35729 * Gets the maximum size for the resizing element
35730 * @return {Number} The maximum size
35732 getMaximumSize : function(){
35733 return this.maxSize;
35737 * Sets the maximum size for the resizing element
35738 * @param {Number} maxSize The maximum size
35740 setMaximumSize : function(maxSize){
35741 this.maxSize = maxSize;
35745 * Sets the initialize size for the resizing element
35746 * @param {Number} size The initial size
35748 setCurrentSize : function(size){
35749 var oldAnimate = this.animate;
35750 this.animate = false;
35751 this.adapter.setElementSize(this, size);
35752 this.animate = oldAnimate;
35756 * Destroy this splitbar.
35757 * @param {Boolean} removeEl True to remove the element
35759 destroy : function(removeEl){
35761 this.shim.remove();
35764 this.proxy.parentNode.removeChild(this.proxy);
35772 * @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.
35774 Roo.bootstrap.SplitBar.createProxy = function(dir){
35775 var proxy = new Roo.Element(document.createElement("div"));
35776 proxy.unselectable();
35777 var cls = 'roo-splitbar-proxy';
35778 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35779 document.body.appendChild(proxy.dom);
35784 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35785 * Default Adapter. It assumes the splitter and resizing element are not positioned
35786 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35788 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35791 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35792 // do nothing for now
35793 init : function(s){
35797 * Called before drag operations to get the current size of the resizing element.
35798 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35800 getElementSize : function(s){
35801 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35802 return s.resizingEl.getWidth();
35804 return s.resizingEl.getHeight();
35809 * Called after drag operations to set the size of the resizing element.
35810 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35811 * @param {Number} newSize The new size to set
35812 * @param {Function} onComplete A function to be invoked when resizing is complete
35814 setElementSize : function(s, newSize, onComplete){
35815 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35817 s.resizingEl.setWidth(newSize);
35819 onComplete(s, newSize);
35822 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35827 s.resizingEl.setHeight(newSize);
35829 onComplete(s, newSize);
35832 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35839 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35840 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35841 * Adapter that moves the splitter element to align with the resized sizing element.
35842 * Used with an absolute positioned SplitBar.
35843 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35844 * document.body, make sure you assign an id to the body element.
35846 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35847 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35848 this.container = Roo.get(container);
35851 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35852 init : function(s){
35853 this.basic.init(s);
35856 getElementSize : function(s){
35857 return this.basic.getElementSize(s);
35860 setElementSize : function(s, newSize, onComplete){
35861 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35864 moveSplitter : function(s){
35865 var yes = Roo.bootstrap.SplitBar;
35866 switch(s.placement){
35868 s.el.setX(s.resizingEl.getRight());
35871 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35874 s.el.setY(s.resizingEl.getBottom());
35877 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35884 * Orientation constant - Create a vertical SplitBar
35888 Roo.bootstrap.SplitBar.VERTICAL = 1;
35891 * Orientation constant - Create a horizontal SplitBar
35895 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35898 * Placement constant - The resizing element is to the left of the splitter element
35902 Roo.bootstrap.SplitBar.LEFT = 1;
35905 * Placement constant - The resizing element is to the right of the splitter element
35909 Roo.bootstrap.SplitBar.RIGHT = 2;
35912 * Placement constant - The resizing element is positioned above the splitter element
35916 Roo.bootstrap.SplitBar.TOP = 3;
35919 * Placement constant - The resizing element is positioned under splitter element
35923 Roo.bootstrap.SplitBar.BOTTOM = 4;
35924 Roo.namespace("Roo.bootstrap.layout");/*
35926 * Ext JS Library 1.1.1
35927 * Copyright(c) 2006-2007, Ext JS, LLC.
35929 * Originally Released Under LGPL - original licence link has changed is not relivant.
35932 * <script type="text/javascript">
35936 * @class Roo.bootstrap.layout.Manager
35937 * @extends Roo.bootstrap.Component
35938 * Base class for layout managers.
35940 Roo.bootstrap.layout.Manager = function(config)
35942 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35948 /** false to disable window resize monitoring @type Boolean */
35949 this.monitorWindowResize = true;
35954 * Fires when a layout is performed.
35955 * @param {Roo.LayoutManager} this
35959 * @event regionresized
35960 * Fires when the user resizes a region.
35961 * @param {Roo.LayoutRegion} region The resized region
35962 * @param {Number} newSize The new size (width for east/west, height for north/south)
35964 "regionresized" : true,
35966 * @event regioncollapsed
35967 * Fires when a region is collapsed.
35968 * @param {Roo.LayoutRegion} region The collapsed region
35970 "regioncollapsed" : true,
35972 * @event regionexpanded
35973 * Fires when a region is expanded.
35974 * @param {Roo.LayoutRegion} region The expanded region
35976 "regionexpanded" : true
35978 this.updating = false;
35981 this.el = Roo.get(config.el);
35987 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35992 monitorWindowResize : true,
35998 onRender : function(ct, position)
36001 this.el = Roo.get(ct);
36004 //this.fireEvent('render',this);
36008 initEvents: function()
36012 // ie scrollbar fix
36013 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36014 document.body.scroll = "no";
36015 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36016 this.el.position('relative');
36018 this.id = this.el.id;
36019 this.el.addClass("roo-layout-container");
36020 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36021 if(this.el.dom != document.body ) {
36022 this.el.on('resize', this.layout,this);
36023 this.el.on('show', this.layout,this);
36029 * Returns true if this layout is currently being updated
36030 * @return {Boolean}
36032 isUpdating : function(){
36033 return this.updating;
36037 * Suspend the LayoutManager from doing auto-layouts while
36038 * making multiple add or remove calls
36040 beginUpdate : function(){
36041 this.updating = true;
36045 * Restore auto-layouts and optionally disable the manager from performing a layout
36046 * @param {Boolean} noLayout true to disable a layout update
36048 endUpdate : function(noLayout){
36049 this.updating = false;
36055 layout: function(){
36059 onRegionResized : function(region, newSize){
36060 this.fireEvent("regionresized", region, newSize);
36064 onRegionCollapsed : function(region){
36065 this.fireEvent("regioncollapsed", region);
36068 onRegionExpanded : function(region){
36069 this.fireEvent("regionexpanded", region);
36073 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36074 * performs box-model adjustments.
36075 * @return {Object} The size as an object {width: (the width), height: (the height)}
36077 getViewSize : function()
36080 if(this.el.dom != document.body){
36081 size = this.el.getSize();
36083 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36085 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36086 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36091 * Returns the Element this layout is bound to.
36092 * @return {Roo.Element}
36094 getEl : function(){
36099 * Returns the specified region.
36100 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36101 * @return {Roo.LayoutRegion}
36103 getRegion : function(target){
36104 return this.regions[target.toLowerCase()];
36107 onWindowResize : function(){
36108 if(this.monitorWindowResize){
36115 * Ext JS Library 1.1.1
36116 * Copyright(c) 2006-2007, Ext JS, LLC.
36118 * Originally Released Under LGPL - original licence link has changed is not relivant.
36121 * <script type="text/javascript">
36124 * @class Roo.bootstrap.layout.Border
36125 * @extends Roo.bootstrap.layout.Manager
36126 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36127 * please see: examples/bootstrap/nested.html<br><br>
36129 <b>The container the layout is rendered into can be either the body element or any other element.
36130 If it is not the body element, the container needs to either be an absolute positioned element,
36131 or you will need to add "position:relative" to the css of the container. You will also need to specify
36132 the container size if it is not the body element.</b>
36135 * Create a new Border
36136 * @param {Object} config Configuration options
36138 Roo.bootstrap.layout.Border = function(config){
36139 config = config || {};
36140 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36144 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36145 if(config[region]){
36146 config[region].region = region;
36147 this.addRegion(config[region]);
36153 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36155 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36157 parent : false, // this might point to a 'nest' or a ???
36160 * Creates and adds a new region if it doesn't already exist.
36161 * @param {String} target The target region key (north, south, east, west or center).
36162 * @param {Object} config The regions config object
36163 * @return {BorderLayoutRegion} The new region
36165 addRegion : function(config)
36167 if(!this.regions[config.region]){
36168 var r = this.factory(config);
36169 this.bindRegion(r);
36171 return this.regions[config.region];
36175 bindRegion : function(r){
36176 this.regions[r.config.region] = r;
36178 r.on("visibilitychange", this.layout, this);
36179 r.on("paneladded", this.layout, this);
36180 r.on("panelremoved", this.layout, this);
36181 r.on("invalidated", this.layout, this);
36182 r.on("resized", this.onRegionResized, this);
36183 r.on("collapsed", this.onRegionCollapsed, this);
36184 r.on("expanded", this.onRegionExpanded, this);
36188 * Performs a layout update.
36190 layout : function()
36192 if(this.updating) {
36196 // render all the rebions if they have not been done alreayd?
36197 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36198 if(this.regions[region] && !this.regions[region].bodyEl){
36199 this.regions[region].onRender(this.el)
36203 var size = this.getViewSize();
36204 var w = size.width;
36205 var h = size.height;
36210 //var x = 0, y = 0;
36212 var rs = this.regions;
36213 var north = rs["north"];
36214 var south = rs["south"];
36215 var west = rs["west"];
36216 var east = rs["east"];
36217 var center = rs["center"];
36218 //if(this.hideOnLayout){ // not supported anymore
36219 //c.el.setStyle("display", "none");
36221 if(north && north.isVisible()){
36222 var b = north.getBox();
36223 var m = north.getMargins();
36224 b.width = w - (m.left+m.right);
36227 centerY = b.height + b.y + m.bottom;
36228 centerH -= centerY;
36229 north.updateBox(this.safeBox(b));
36231 if(south && south.isVisible()){
36232 var b = south.getBox();
36233 var m = south.getMargins();
36234 b.width = w - (m.left+m.right);
36236 var totalHeight = (b.height + m.top + m.bottom);
36237 b.y = h - totalHeight + m.top;
36238 centerH -= totalHeight;
36239 south.updateBox(this.safeBox(b));
36241 if(west && west.isVisible()){
36242 var b = west.getBox();
36243 var m = west.getMargins();
36244 b.height = centerH - (m.top+m.bottom);
36246 b.y = centerY + m.top;
36247 var totalWidth = (b.width + m.left + m.right);
36248 centerX += totalWidth;
36249 centerW -= totalWidth;
36250 west.updateBox(this.safeBox(b));
36252 if(east && east.isVisible()){
36253 var b = east.getBox();
36254 var m = east.getMargins();
36255 b.height = centerH - (m.top+m.bottom);
36256 var totalWidth = (b.width + m.left + m.right);
36257 b.x = w - totalWidth + m.left;
36258 b.y = centerY + m.top;
36259 centerW -= totalWidth;
36260 east.updateBox(this.safeBox(b));
36263 var m = center.getMargins();
36265 x: centerX + m.left,
36266 y: centerY + m.top,
36267 width: centerW - (m.left+m.right),
36268 height: centerH - (m.top+m.bottom)
36270 //if(this.hideOnLayout){
36271 //center.el.setStyle("display", "block");
36273 center.updateBox(this.safeBox(centerBox));
36276 this.fireEvent("layout", this);
36280 safeBox : function(box){
36281 box.width = Math.max(0, box.width);
36282 box.height = Math.max(0, box.height);
36287 * Adds a ContentPanel (or subclass) to this layout.
36288 * @param {String} target The target region key (north, south, east, west or center).
36289 * @param {Roo.ContentPanel} panel The panel to add
36290 * @return {Roo.ContentPanel} The added panel
36292 add : function(target, panel){
36294 target = target.toLowerCase();
36295 return this.regions[target].add(panel);
36299 * Remove a ContentPanel (or subclass) to this layout.
36300 * @param {String} target The target region key (north, south, east, west or center).
36301 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36302 * @return {Roo.ContentPanel} The removed panel
36304 remove : function(target, panel){
36305 target = target.toLowerCase();
36306 return this.regions[target].remove(panel);
36310 * Searches all regions for a panel with the specified id
36311 * @param {String} panelId
36312 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36314 findPanel : function(panelId){
36315 var rs = this.regions;
36316 for(var target in rs){
36317 if(typeof rs[target] != "function"){
36318 var p = rs[target].getPanel(panelId);
36328 * Searches all regions for a panel with the specified id and activates (shows) it.
36329 * @param {String/ContentPanel} panelId The panels id or the panel itself
36330 * @return {Roo.ContentPanel} The shown panel or null
36332 showPanel : function(panelId) {
36333 var rs = this.regions;
36334 for(var target in rs){
36335 var r = rs[target];
36336 if(typeof r != "function"){
36337 if(r.hasPanel(panelId)){
36338 return r.showPanel(panelId);
36346 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36347 * @param {Roo.state.Provider} provider (optional) An alternate state provider
36350 restoreState : function(provider){
36352 provider = Roo.state.Manager;
36354 var sm = new Roo.LayoutStateManager();
36355 sm.init(this, provider);
36361 * Adds a xtype elements to the layout.
36365 xtype : 'ContentPanel',
36372 xtype : 'NestedLayoutPanel',
36378 items : [ ... list of content panels or nested layout panels.. ]
36382 * @param {Object} cfg Xtype definition of item to add.
36384 addxtype : function(cfg)
36386 // basically accepts a pannel...
36387 // can accept a layout region..!?!?
36388 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36391 // theory? children can only be panels??
36393 //if (!cfg.xtype.match(/Panel$/)) {
36398 if (typeof(cfg.region) == 'undefined') {
36399 Roo.log("Failed to add Panel, region was not set");
36403 var region = cfg.region;
36409 xitems = cfg.items;
36414 if ( region == 'center') {
36415 Roo.log("Center: " + cfg.title);
36421 case 'Content': // ContentPanel (el, cfg)
36422 case 'Scroll': // ContentPanel (el, cfg)
36424 cfg.autoCreate = cfg.autoCreate || true;
36425 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36427 // var el = this.el.createChild();
36428 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36431 this.add(region, ret);
36435 case 'TreePanel': // our new panel!
36436 cfg.el = this.el.createChild();
36437 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36438 this.add(region, ret);
36443 // create a new Layout (which is a Border Layout...
36445 var clayout = cfg.layout;
36446 clayout.el = this.el.createChild();
36447 clayout.items = clayout.items || [];
36451 // replace this exitems with the clayout ones..
36452 xitems = clayout.items;
36454 // force background off if it's in center...
36455 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36456 cfg.background = false;
36458 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
36461 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36462 //console.log('adding nested layout panel ' + cfg.toSource());
36463 this.add(region, ret);
36464 nb = {}; /// find first...
36469 // needs grid and region
36471 //var el = this.getRegion(region).el.createChild();
36473 *var el = this.el.createChild();
36474 // create the grid first...
36475 cfg.grid.container = el;
36476 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36479 if (region == 'center' && this.active ) {
36480 cfg.background = false;
36483 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36485 this.add(region, ret);
36487 if (cfg.background) {
36488 // render grid on panel activation (if panel background)
36489 ret.on('activate', function(gp) {
36490 if (!gp.grid.rendered) {
36491 // gp.grid.render(el);
36495 // cfg.grid.render(el);
36501 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36502 // it was the old xcomponent building that caused this before.
36503 // espeically if border is the top element in the tree.
36513 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36515 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36516 this.add(region, ret);
36520 throw "Can not add '" + cfg.xtype + "' to Border";
36526 this.beginUpdate();
36530 Roo.each(xitems, function(i) {
36531 region = nb && i.region ? i.region : false;
36533 var add = ret.addxtype(i);
36536 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36537 if (!i.background) {
36538 abn[region] = nb[region] ;
36545 // make the last non-background panel active..
36546 //if (nb) { Roo.log(abn); }
36549 for(var r in abn) {
36550 region = this.getRegion(r);
36552 // tried using nb[r], but it does not work..
36554 region.showPanel(abn[r]);
36565 factory : function(cfg)
36568 var validRegions = Roo.bootstrap.layout.Border.regions;
36570 var target = cfg.region;
36573 var r = Roo.bootstrap.layout;
36577 return new r.North(cfg);
36579 return new r.South(cfg);
36581 return new r.East(cfg);
36583 return new r.West(cfg);
36585 return new r.Center(cfg);
36587 throw 'Layout region "'+target+'" not supported.';
36594 * Ext JS Library 1.1.1
36595 * Copyright(c) 2006-2007, Ext JS, LLC.
36597 * Originally Released Under LGPL - original licence link has changed is not relivant.
36600 * <script type="text/javascript">
36604 * @class Roo.bootstrap.layout.Basic
36605 * @extends Roo.util.Observable
36606 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36607 * and does not have a titlebar, tabs or any other features. All it does is size and position
36608 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36609 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36610 * @cfg {string} region the region that it inhabits..
36611 * @cfg {bool} skipConfig skip config?
36615 Roo.bootstrap.layout.Basic = function(config){
36617 this.mgr = config.mgr;
36619 this.position = config.region;
36621 var skipConfig = config.skipConfig;
36625 * @scope Roo.BasicLayoutRegion
36629 * @event beforeremove
36630 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36631 * @param {Roo.LayoutRegion} this
36632 * @param {Roo.ContentPanel} panel The panel
36633 * @param {Object} e The cancel event object
36635 "beforeremove" : true,
36637 * @event invalidated
36638 * Fires when the layout for this region is changed.
36639 * @param {Roo.LayoutRegion} this
36641 "invalidated" : true,
36643 * @event visibilitychange
36644 * Fires when this region is shown or hidden
36645 * @param {Roo.LayoutRegion} this
36646 * @param {Boolean} visibility true or false
36648 "visibilitychange" : true,
36650 * @event paneladded
36651 * Fires when a panel is added.
36652 * @param {Roo.LayoutRegion} this
36653 * @param {Roo.ContentPanel} panel The panel
36655 "paneladded" : true,
36657 * @event panelremoved
36658 * Fires when a panel is removed.
36659 * @param {Roo.LayoutRegion} this
36660 * @param {Roo.ContentPanel} panel The panel
36662 "panelremoved" : true,
36664 * @event beforecollapse
36665 * Fires when this region before collapse.
36666 * @param {Roo.LayoutRegion} this
36668 "beforecollapse" : true,
36671 * Fires when this region is collapsed.
36672 * @param {Roo.LayoutRegion} this
36674 "collapsed" : true,
36677 * Fires when this region is expanded.
36678 * @param {Roo.LayoutRegion} this
36683 * Fires when this region is slid into view.
36684 * @param {Roo.LayoutRegion} this
36686 "slideshow" : true,
36689 * Fires when this region slides out of view.
36690 * @param {Roo.LayoutRegion} this
36692 "slidehide" : true,
36694 * @event panelactivated
36695 * Fires when a panel is activated.
36696 * @param {Roo.LayoutRegion} this
36697 * @param {Roo.ContentPanel} panel The activated panel
36699 "panelactivated" : true,
36702 * Fires when the user resizes this region.
36703 * @param {Roo.LayoutRegion} this
36704 * @param {Number} newSize The new size (width for east/west, height for north/south)
36708 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36709 this.panels = new Roo.util.MixedCollection();
36710 this.panels.getKey = this.getPanelId.createDelegate(this);
36712 this.activePanel = null;
36713 // ensure listeners are added...
36715 if (config.listeners || config.events) {
36716 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36717 listeners : config.listeners || {},
36718 events : config.events || {}
36722 if(skipConfig !== true){
36723 this.applyConfig(config);
36727 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36729 getPanelId : function(p){
36733 applyConfig : function(config){
36734 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36735 this.config = config;
36740 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36741 * the width, for horizontal (north, south) the height.
36742 * @param {Number} newSize The new width or height
36744 resizeTo : function(newSize){
36745 var el = this.el ? this.el :
36746 (this.activePanel ? this.activePanel.getEl() : null);
36748 switch(this.position){
36751 el.setWidth(newSize);
36752 this.fireEvent("resized", this, newSize);
36756 el.setHeight(newSize);
36757 this.fireEvent("resized", this, newSize);
36763 getBox : function(){
36764 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36767 getMargins : function(){
36768 return this.margins;
36771 updateBox : function(box){
36773 var el = this.activePanel.getEl();
36774 el.dom.style.left = box.x + "px";
36775 el.dom.style.top = box.y + "px";
36776 this.activePanel.setSize(box.width, box.height);
36780 * Returns the container element for this region.
36781 * @return {Roo.Element}
36783 getEl : function(){
36784 return this.activePanel;
36788 * Returns true if this region is currently visible.
36789 * @return {Boolean}
36791 isVisible : function(){
36792 return this.activePanel ? true : false;
36795 setActivePanel : function(panel){
36796 panel = this.getPanel(panel);
36797 if(this.activePanel && this.activePanel != panel){
36798 this.activePanel.setActiveState(false);
36799 this.activePanel.getEl().setLeftTop(-10000,-10000);
36801 this.activePanel = panel;
36802 panel.setActiveState(true);
36804 panel.setSize(this.box.width, this.box.height);
36806 this.fireEvent("panelactivated", this, panel);
36807 this.fireEvent("invalidated");
36811 * Show the specified panel.
36812 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36813 * @return {Roo.ContentPanel} The shown panel or null
36815 showPanel : function(panel){
36816 panel = this.getPanel(panel);
36818 this.setActivePanel(panel);
36824 * Get the active panel for this region.
36825 * @return {Roo.ContentPanel} The active panel or null
36827 getActivePanel : function(){
36828 return this.activePanel;
36832 * Add the passed ContentPanel(s)
36833 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36834 * @return {Roo.ContentPanel} The panel added (if only one was added)
36836 add : function(panel){
36837 if(arguments.length > 1){
36838 for(var i = 0, len = arguments.length; i < len; i++) {
36839 this.add(arguments[i]);
36843 if(this.hasPanel(panel)){
36844 this.showPanel(panel);
36847 var el = panel.getEl();
36848 if(el.dom.parentNode != this.mgr.el.dom){
36849 this.mgr.el.dom.appendChild(el.dom);
36851 if(panel.setRegion){
36852 panel.setRegion(this);
36854 this.panels.add(panel);
36855 el.setStyle("position", "absolute");
36856 if(!panel.background){
36857 this.setActivePanel(panel);
36858 if(this.config.initialSize && this.panels.getCount()==1){
36859 this.resizeTo(this.config.initialSize);
36862 this.fireEvent("paneladded", this, panel);
36867 * Returns true if the panel is in this region.
36868 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36869 * @return {Boolean}
36871 hasPanel : function(panel){
36872 if(typeof panel == "object"){ // must be panel obj
36873 panel = panel.getId();
36875 return this.getPanel(panel) ? true : false;
36879 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36880 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36881 * @param {Boolean} preservePanel Overrides the config preservePanel option
36882 * @return {Roo.ContentPanel} The panel that was removed
36884 remove : function(panel, preservePanel){
36885 panel = this.getPanel(panel);
36890 this.fireEvent("beforeremove", this, panel, e);
36891 if(e.cancel === true){
36894 var panelId = panel.getId();
36895 this.panels.removeKey(panelId);
36900 * Returns the panel specified or null if it's not in this region.
36901 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36902 * @return {Roo.ContentPanel}
36904 getPanel : function(id){
36905 if(typeof id == "object"){ // must be panel obj
36908 return this.panels.get(id);
36912 * Returns this regions position (north/south/east/west/center).
36915 getPosition: function(){
36916 return this.position;
36920 * Ext JS Library 1.1.1
36921 * Copyright(c) 2006-2007, Ext JS, LLC.
36923 * Originally Released Under LGPL - original licence link has changed is not relivant.
36926 * <script type="text/javascript">
36930 * @class Roo.bootstrap.layout.Region
36931 * @extends Roo.bootstrap.layout.Basic
36932 * This class represents a region in a layout manager.
36934 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36935 * @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})
36936 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36937 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36938 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36939 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36940 * @cfg {String} title The title for the region (overrides panel titles)
36941 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36942 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36943 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36944 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36945 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36946 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36947 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36948 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36949 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36950 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36952 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36953 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36954 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36955 * @cfg {Number} width For East/West panels
36956 * @cfg {Number} height For North/South panels
36957 * @cfg {Boolean} split To show the splitter
36958 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36960 * @cfg {string} cls Extra CSS classes to add to region
36962 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36963 * @cfg {string} region the region that it inhabits..
36966 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36967 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36969 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36970 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36971 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36973 Roo.bootstrap.layout.Region = function(config)
36975 this.applyConfig(config);
36977 var mgr = config.mgr;
36978 var pos = config.region;
36979 config.skipConfig = true;
36980 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36983 this.onRender(mgr.el);
36986 this.visible = true;
36987 this.collapsed = false;
36988 this.unrendered_panels = [];
36991 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36993 position: '', // set by wrapper (eg. north/south etc..)
36994 unrendered_panels : null, // unrendered panels.
36996 tabPosition : false,
36998 mgr: false, // points to 'Border'
37001 createBody : function(){
37002 /** This region's body element
37003 * @type Roo.Element */
37004 this.bodyEl = this.el.createChild({
37006 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37010 onRender: function(ctr, pos)
37012 var dh = Roo.DomHelper;
37013 /** This region's container element
37014 * @type Roo.Element */
37015 this.el = dh.append(ctr.dom, {
37017 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37019 /** This region's title element
37020 * @type Roo.Element */
37022 this.titleEl = dh.append(this.el.dom, {
37024 unselectable: "on",
37025 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37027 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37028 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37032 this.titleEl.enableDisplayMode();
37033 /** This region's title text element
37034 * @type HTMLElement */
37035 this.titleTextEl = this.titleEl.dom.firstChild;
37036 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37038 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37039 this.closeBtn.enableDisplayMode();
37040 this.closeBtn.on("click", this.closeClicked, this);
37041 this.closeBtn.hide();
37043 this.createBody(this.config);
37044 if(this.config.hideWhenEmpty){
37046 this.on("paneladded", this.validateVisibility, this);
37047 this.on("panelremoved", this.validateVisibility, this);
37049 if(this.autoScroll){
37050 this.bodyEl.setStyle("overflow", "auto");
37052 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37054 //if(c.titlebar !== false){
37055 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37056 this.titleEl.hide();
37058 this.titleEl.show();
37059 if(this.config.title){
37060 this.titleTextEl.innerHTML = this.config.title;
37064 if(this.config.collapsed){
37065 this.collapse(true);
37067 if(this.config.hidden){
37071 if (this.unrendered_panels && this.unrendered_panels.length) {
37072 for (var i =0;i< this.unrendered_panels.length; i++) {
37073 this.add(this.unrendered_panels[i]);
37075 this.unrendered_panels = null;
37081 applyConfig : function(c)
37084 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37085 var dh = Roo.DomHelper;
37086 if(c.titlebar !== false){
37087 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37088 this.collapseBtn.on("click", this.collapse, this);
37089 this.collapseBtn.enableDisplayMode();
37091 if(c.showPin === true || this.showPin){
37092 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37093 this.stickBtn.enableDisplayMode();
37094 this.stickBtn.on("click", this.expand, this);
37095 this.stickBtn.hide();
37100 /** This region's collapsed element
37101 * @type Roo.Element */
37104 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37105 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37108 if(c.floatable !== false){
37109 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37110 this.collapsedEl.on("click", this.collapseClick, this);
37113 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37114 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37115 id: "message", unselectable: "on", style:{"float":"left"}});
37116 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37118 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37119 this.expandBtn.on("click", this.expand, this);
37123 if(this.collapseBtn){
37124 this.collapseBtn.setVisible(c.collapsible == true);
37127 this.cmargins = c.cmargins || this.cmargins ||
37128 (this.position == "west" || this.position == "east" ?
37129 {top: 0, left: 2, right:2, bottom: 0} :
37130 {top: 2, left: 0, right:0, bottom: 2});
37132 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37135 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37137 this.autoScroll = c.autoScroll || false;
37142 this.duration = c.duration || .30;
37143 this.slideDuration = c.slideDuration || .45;
37148 * Returns true if this region is currently visible.
37149 * @return {Boolean}
37151 isVisible : function(){
37152 return this.visible;
37156 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37157 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37159 //setCollapsedTitle : function(title){
37160 // title = title || " ";
37161 // if(this.collapsedTitleTextEl){
37162 // this.collapsedTitleTextEl.innerHTML = title;
37166 getBox : function(){
37168 // if(!this.collapsed){
37169 b = this.el.getBox(false, true);
37171 // b = this.collapsedEl.getBox(false, true);
37176 getMargins : function(){
37177 return this.margins;
37178 //return this.collapsed ? this.cmargins : this.margins;
37181 highlight : function(){
37182 this.el.addClass("x-layout-panel-dragover");
37185 unhighlight : function(){
37186 this.el.removeClass("x-layout-panel-dragover");
37189 updateBox : function(box)
37191 if (!this.bodyEl) {
37192 return; // not rendered yet..
37196 if(!this.collapsed){
37197 this.el.dom.style.left = box.x + "px";
37198 this.el.dom.style.top = box.y + "px";
37199 this.updateBody(box.width, box.height);
37201 this.collapsedEl.dom.style.left = box.x + "px";
37202 this.collapsedEl.dom.style.top = box.y + "px";
37203 this.collapsedEl.setSize(box.width, box.height);
37206 this.tabs.autoSizeTabs();
37210 updateBody : function(w, h)
37213 this.el.setWidth(w);
37214 w -= this.el.getBorderWidth("rl");
37215 if(this.config.adjustments){
37216 w += this.config.adjustments[0];
37219 if(h !== null && h > 0){
37220 this.el.setHeight(h);
37221 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37222 h -= this.el.getBorderWidth("tb");
37223 if(this.config.adjustments){
37224 h += this.config.adjustments[1];
37226 this.bodyEl.setHeight(h);
37228 h = this.tabs.syncHeight(h);
37231 if(this.panelSize){
37232 w = w !== null ? w : this.panelSize.width;
37233 h = h !== null ? h : this.panelSize.height;
37235 if(this.activePanel){
37236 var el = this.activePanel.getEl();
37237 w = w !== null ? w : el.getWidth();
37238 h = h !== null ? h : el.getHeight();
37239 this.panelSize = {width: w, height: h};
37240 this.activePanel.setSize(w, h);
37242 if(Roo.isIE && this.tabs){
37243 this.tabs.el.repaint();
37248 * Returns the container element for this region.
37249 * @return {Roo.Element}
37251 getEl : function(){
37256 * Hides this region.
37259 //if(!this.collapsed){
37260 this.el.dom.style.left = "-2000px";
37263 // this.collapsedEl.dom.style.left = "-2000px";
37264 // this.collapsedEl.hide();
37266 this.visible = false;
37267 this.fireEvent("visibilitychange", this, false);
37271 * Shows this region if it was previously hidden.
37274 //if(!this.collapsed){
37277 // this.collapsedEl.show();
37279 this.visible = true;
37280 this.fireEvent("visibilitychange", this, true);
37283 closeClicked : function(){
37284 if(this.activePanel){
37285 this.remove(this.activePanel);
37289 collapseClick : function(e){
37291 e.stopPropagation();
37294 e.stopPropagation();
37300 * Collapses this region.
37301 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37304 collapse : function(skipAnim, skipCheck = false){
37305 if(this.collapsed) {
37309 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37311 this.collapsed = true;
37313 this.split.el.hide();
37315 if(this.config.animate && skipAnim !== true){
37316 this.fireEvent("invalidated", this);
37317 this.animateCollapse();
37319 this.el.setLocation(-20000,-20000);
37321 this.collapsedEl.show();
37322 this.fireEvent("collapsed", this);
37323 this.fireEvent("invalidated", this);
37329 animateCollapse : function(){
37334 * Expands this region if it was previously collapsed.
37335 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37336 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37339 expand : function(e, skipAnim){
37341 e.stopPropagation();
37343 if(!this.collapsed || this.el.hasActiveFx()) {
37347 this.afterSlideIn();
37350 this.collapsed = false;
37351 if(this.config.animate && skipAnim !== true){
37352 this.animateExpand();
37356 this.split.el.show();
37358 this.collapsedEl.setLocation(-2000,-2000);
37359 this.collapsedEl.hide();
37360 this.fireEvent("invalidated", this);
37361 this.fireEvent("expanded", this);
37365 animateExpand : function(){
37369 initTabs : function()
37371 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37373 var ts = new Roo.bootstrap.panel.Tabs({
37374 el: this.bodyEl.dom,
37376 tabPosition: this.tabPosition ? this.tabPosition : 'top',
37377 disableTooltips: this.config.disableTabTips,
37378 toolbar : this.config.toolbar
37381 if(this.config.hideTabs){
37382 ts.stripWrap.setDisplayed(false);
37385 ts.resizeTabs = this.config.resizeTabs === true;
37386 ts.minTabWidth = this.config.minTabWidth || 40;
37387 ts.maxTabWidth = this.config.maxTabWidth || 250;
37388 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37389 ts.monitorResize = false;
37390 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37391 ts.bodyEl.addClass('roo-layout-tabs-body');
37392 this.panels.each(this.initPanelAsTab, this);
37395 initPanelAsTab : function(panel){
37396 var ti = this.tabs.addTab(
37400 this.config.closeOnTab && panel.isClosable(),
37403 if(panel.tabTip !== undefined){
37404 ti.setTooltip(panel.tabTip);
37406 ti.on("activate", function(){
37407 this.setActivePanel(panel);
37410 if(this.config.closeOnTab){
37411 ti.on("beforeclose", function(t, e){
37413 this.remove(panel);
37417 panel.tabItem = ti;
37422 updatePanelTitle : function(panel, title)
37424 if(this.activePanel == panel){
37425 this.updateTitle(title);
37428 var ti = this.tabs.getTab(panel.getEl().id);
37430 if(panel.tabTip !== undefined){
37431 ti.setTooltip(panel.tabTip);
37436 updateTitle : function(title){
37437 if(this.titleTextEl && !this.config.title){
37438 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
37442 setActivePanel : function(panel)
37444 panel = this.getPanel(panel);
37445 if(this.activePanel && this.activePanel != panel){
37446 if(this.activePanel.setActiveState(false) === false){
37450 this.activePanel = panel;
37451 panel.setActiveState(true);
37452 if(this.panelSize){
37453 panel.setSize(this.panelSize.width, this.panelSize.height);
37456 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37458 this.updateTitle(panel.getTitle());
37460 this.fireEvent("invalidated", this);
37462 this.fireEvent("panelactivated", this, panel);
37466 * Shows the specified panel.
37467 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37468 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37470 showPanel : function(panel)
37472 panel = this.getPanel(panel);
37475 var tab = this.tabs.getTab(panel.getEl().id);
37476 if(tab.isHidden()){
37477 this.tabs.unhideTab(tab.id);
37481 this.setActivePanel(panel);
37488 * Get the active panel for this region.
37489 * @return {Roo.ContentPanel} The active panel or null
37491 getActivePanel : function(){
37492 return this.activePanel;
37495 validateVisibility : function(){
37496 if(this.panels.getCount() < 1){
37497 this.updateTitle(" ");
37498 this.closeBtn.hide();
37501 if(!this.isVisible()){
37508 * Adds the passed ContentPanel(s) to this region.
37509 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37510 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37512 add : function(panel)
37514 if(arguments.length > 1){
37515 for(var i = 0, len = arguments.length; i < len; i++) {
37516 this.add(arguments[i]);
37521 // if we have not been rendered yet, then we can not really do much of this..
37522 if (!this.bodyEl) {
37523 this.unrendered_panels.push(panel);
37530 if(this.hasPanel(panel)){
37531 this.showPanel(panel);
37534 panel.setRegion(this);
37535 this.panels.add(panel);
37536 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37537 // sinle panel - no tab...?? would it not be better to render it with the tabs,
37538 // and hide them... ???
37539 this.bodyEl.dom.appendChild(panel.getEl().dom);
37540 if(panel.background !== true){
37541 this.setActivePanel(panel);
37543 this.fireEvent("paneladded", this, panel);
37550 this.initPanelAsTab(panel);
37554 if(panel.background !== true){
37555 this.tabs.activate(panel.getEl().id);
37557 this.fireEvent("paneladded", this, panel);
37562 * Hides the tab for the specified panel.
37563 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37565 hidePanel : function(panel){
37566 if(this.tabs && (panel = this.getPanel(panel))){
37567 this.tabs.hideTab(panel.getEl().id);
37572 * Unhides the tab for a previously hidden panel.
37573 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37575 unhidePanel : function(panel){
37576 if(this.tabs && (panel = this.getPanel(panel))){
37577 this.tabs.unhideTab(panel.getEl().id);
37581 clearPanels : function(){
37582 while(this.panels.getCount() > 0){
37583 this.remove(this.panels.first());
37588 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37589 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37590 * @param {Boolean} preservePanel Overrides the config preservePanel option
37591 * @return {Roo.ContentPanel} The panel that was removed
37593 remove : function(panel, preservePanel)
37595 panel = this.getPanel(panel);
37600 this.fireEvent("beforeremove", this, panel, e);
37601 if(e.cancel === true){
37604 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37605 var panelId = panel.getId();
37606 this.panels.removeKey(panelId);
37608 document.body.appendChild(panel.getEl().dom);
37611 this.tabs.removeTab(panel.getEl().id);
37612 }else if (!preservePanel){
37613 this.bodyEl.dom.removeChild(panel.getEl().dom);
37615 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37616 var p = this.panels.first();
37617 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37618 tempEl.appendChild(p.getEl().dom);
37619 this.bodyEl.update("");
37620 this.bodyEl.dom.appendChild(p.getEl().dom);
37622 this.updateTitle(p.getTitle());
37624 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37625 this.setActivePanel(p);
37627 panel.setRegion(null);
37628 if(this.activePanel == panel){
37629 this.activePanel = null;
37631 if(this.config.autoDestroy !== false && preservePanel !== true){
37632 try{panel.destroy();}catch(e){}
37634 this.fireEvent("panelremoved", this, panel);
37639 * Returns the TabPanel component used by this region
37640 * @return {Roo.TabPanel}
37642 getTabs : function(){
37646 createTool : function(parentEl, className){
37647 var btn = Roo.DomHelper.append(parentEl, {
37649 cls: "x-layout-tools-button",
37652 cls: "roo-layout-tools-button-inner " + className,
37656 btn.addClassOnOver("roo-layout-tools-button-over");
37661 * Ext JS Library 1.1.1
37662 * Copyright(c) 2006-2007, Ext JS, LLC.
37664 * Originally Released Under LGPL - original licence link has changed is not relivant.
37667 * <script type="text/javascript">
37673 * @class Roo.SplitLayoutRegion
37674 * @extends Roo.LayoutRegion
37675 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37677 Roo.bootstrap.layout.Split = function(config){
37678 this.cursor = config.cursor;
37679 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37682 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37684 splitTip : "Drag to resize.",
37685 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37686 useSplitTips : false,
37688 applyConfig : function(config){
37689 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37692 onRender : function(ctr,pos) {
37694 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37695 if(!this.config.split){
37700 var splitEl = Roo.DomHelper.append(ctr.dom, {
37702 id: this.el.id + "-split",
37703 cls: "roo-layout-split roo-layout-split-"+this.position,
37706 /** The SplitBar for this region
37707 * @type Roo.SplitBar */
37708 // does not exist yet...
37709 Roo.log([this.position, this.orientation]);
37711 this.split = new Roo.bootstrap.SplitBar({
37712 dragElement : splitEl,
37713 resizingElement: this.el,
37714 orientation : this.orientation
37717 this.split.on("moved", this.onSplitMove, this);
37718 this.split.useShim = this.config.useShim === true;
37719 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37720 if(this.useSplitTips){
37721 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37723 //if(config.collapsible){
37724 // this.split.el.on("dblclick", this.collapse, this);
37727 if(typeof this.config.minSize != "undefined"){
37728 this.split.minSize = this.config.minSize;
37730 if(typeof this.config.maxSize != "undefined"){
37731 this.split.maxSize = this.config.maxSize;
37733 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37734 this.hideSplitter();
37739 getHMaxSize : function(){
37740 var cmax = this.config.maxSize || 10000;
37741 var center = this.mgr.getRegion("center");
37742 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37745 getVMaxSize : function(){
37746 var cmax = this.config.maxSize || 10000;
37747 var center = this.mgr.getRegion("center");
37748 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37751 onSplitMove : function(split, newSize){
37752 this.fireEvent("resized", this, newSize);
37756 * Returns the {@link Roo.SplitBar} for this region.
37757 * @return {Roo.SplitBar}
37759 getSplitBar : function(){
37764 this.hideSplitter();
37765 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37768 hideSplitter : function(){
37770 this.split.el.setLocation(-2000,-2000);
37771 this.split.el.hide();
37777 this.split.el.show();
37779 Roo.bootstrap.layout.Split.superclass.show.call(this);
37782 beforeSlide: function(){
37783 if(Roo.isGecko){// firefox overflow auto bug workaround
37784 this.bodyEl.clip();
37786 this.tabs.bodyEl.clip();
37788 if(this.activePanel){
37789 this.activePanel.getEl().clip();
37791 if(this.activePanel.beforeSlide){
37792 this.activePanel.beforeSlide();
37798 afterSlide : function(){
37799 if(Roo.isGecko){// firefox overflow auto bug workaround
37800 this.bodyEl.unclip();
37802 this.tabs.bodyEl.unclip();
37804 if(this.activePanel){
37805 this.activePanel.getEl().unclip();
37806 if(this.activePanel.afterSlide){
37807 this.activePanel.afterSlide();
37813 initAutoHide : function(){
37814 if(this.autoHide !== false){
37815 if(!this.autoHideHd){
37816 var st = new Roo.util.DelayedTask(this.slideIn, this);
37817 this.autoHideHd = {
37818 "mouseout": function(e){
37819 if(!e.within(this.el, true)){
37823 "mouseover" : function(e){
37829 this.el.on(this.autoHideHd);
37833 clearAutoHide : function(){
37834 if(this.autoHide !== false){
37835 this.el.un("mouseout", this.autoHideHd.mouseout);
37836 this.el.un("mouseover", this.autoHideHd.mouseover);
37840 clearMonitor : function(){
37841 Roo.get(document).un("click", this.slideInIf, this);
37844 // these names are backwards but not changed for compat
37845 slideOut : function(){
37846 if(this.isSlid || this.el.hasActiveFx()){
37849 this.isSlid = true;
37850 if(this.collapseBtn){
37851 this.collapseBtn.hide();
37853 this.closeBtnState = this.closeBtn.getStyle('display');
37854 this.closeBtn.hide();
37856 this.stickBtn.show();
37859 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37860 this.beforeSlide();
37861 this.el.setStyle("z-index", 10001);
37862 this.el.slideIn(this.getSlideAnchor(), {
37863 callback: function(){
37865 this.initAutoHide();
37866 Roo.get(document).on("click", this.slideInIf, this);
37867 this.fireEvent("slideshow", this);
37874 afterSlideIn : function(){
37875 this.clearAutoHide();
37876 this.isSlid = false;
37877 this.clearMonitor();
37878 this.el.setStyle("z-index", "");
37879 if(this.collapseBtn){
37880 this.collapseBtn.show();
37882 this.closeBtn.setStyle('display', this.closeBtnState);
37884 this.stickBtn.hide();
37886 this.fireEvent("slidehide", this);
37889 slideIn : function(cb){
37890 if(!this.isSlid || this.el.hasActiveFx()){
37894 this.isSlid = false;
37895 this.beforeSlide();
37896 this.el.slideOut(this.getSlideAnchor(), {
37897 callback: function(){
37898 this.el.setLeftTop(-10000, -10000);
37900 this.afterSlideIn();
37908 slideInIf : function(e){
37909 if(!e.within(this.el)){
37914 animateCollapse : function(){
37915 this.beforeSlide();
37916 this.el.setStyle("z-index", 20000);
37917 var anchor = this.getSlideAnchor();
37918 this.el.slideOut(anchor, {
37919 callback : function(){
37920 this.el.setStyle("z-index", "");
37921 this.collapsedEl.slideIn(anchor, {duration:.3});
37923 this.el.setLocation(-10000,-10000);
37925 this.fireEvent("collapsed", this);
37932 animateExpand : function(){
37933 this.beforeSlide();
37934 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37935 this.el.setStyle("z-index", 20000);
37936 this.collapsedEl.hide({
37939 this.el.slideIn(this.getSlideAnchor(), {
37940 callback : function(){
37941 this.el.setStyle("z-index", "");
37944 this.split.el.show();
37946 this.fireEvent("invalidated", this);
37947 this.fireEvent("expanded", this);
37975 getAnchor : function(){
37976 return this.anchors[this.position];
37979 getCollapseAnchor : function(){
37980 return this.canchors[this.position];
37983 getSlideAnchor : function(){
37984 return this.sanchors[this.position];
37987 getAlignAdj : function(){
37988 var cm = this.cmargins;
37989 switch(this.position){
38005 getExpandAdj : function(){
38006 var c = this.collapsedEl, cm = this.cmargins;
38007 switch(this.position){
38009 return [-(cm.right+c.getWidth()+cm.left), 0];
38012 return [cm.right+c.getWidth()+cm.left, 0];
38015 return [0, -(cm.top+cm.bottom+c.getHeight())];
38018 return [0, cm.top+cm.bottom+c.getHeight()];
38024 * Ext JS Library 1.1.1
38025 * Copyright(c) 2006-2007, Ext JS, LLC.
38027 * Originally Released Under LGPL - original licence link has changed is not relivant.
38030 * <script type="text/javascript">
38033 * These classes are private internal classes
38035 Roo.bootstrap.layout.Center = function(config){
38036 config.region = "center";
38037 Roo.bootstrap.layout.Region.call(this, config);
38038 this.visible = true;
38039 this.minWidth = config.minWidth || 20;
38040 this.minHeight = config.minHeight || 20;
38043 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38045 // center panel can't be hidden
38049 // center panel can't be hidden
38052 getMinWidth: function(){
38053 return this.minWidth;
38056 getMinHeight: function(){
38057 return this.minHeight;
38071 Roo.bootstrap.layout.North = function(config)
38073 config.region = 'north';
38074 config.cursor = 'n-resize';
38076 Roo.bootstrap.layout.Split.call(this, config);
38080 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38081 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38082 this.split.el.addClass("roo-layout-split-v");
38084 var size = config.initialSize || config.height;
38085 if(typeof size != "undefined"){
38086 this.el.setHeight(size);
38089 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38091 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38095 getBox : function(){
38096 if(this.collapsed){
38097 return this.collapsedEl.getBox();
38099 var box = this.el.getBox();
38101 box.height += this.split.el.getHeight();
38106 updateBox : function(box){
38107 if(this.split && !this.collapsed){
38108 box.height -= this.split.el.getHeight();
38109 this.split.el.setLeft(box.x);
38110 this.split.el.setTop(box.y+box.height);
38111 this.split.el.setWidth(box.width);
38113 if(this.collapsed){
38114 this.updateBody(box.width, null);
38116 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38124 Roo.bootstrap.layout.South = function(config){
38125 config.region = 'south';
38126 config.cursor = 's-resize';
38127 Roo.bootstrap.layout.Split.call(this, config);
38129 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38130 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38131 this.split.el.addClass("roo-layout-split-v");
38133 var size = config.initialSize || config.height;
38134 if(typeof size != "undefined"){
38135 this.el.setHeight(size);
38139 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38140 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38141 getBox : function(){
38142 if(this.collapsed){
38143 return this.collapsedEl.getBox();
38145 var box = this.el.getBox();
38147 var sh = this.split.el.getHeight();
38154 updateBox : function(box){
38155 if(this.split && !this.collapsed){
38156 var sh = this.split.el.getHeight();
38159 this.split.el.setLeft(box.x);
38160 this.split.el.setTop(box.y-sh);
38161 this.split.el.setWidth(box.width);
38163 if(this.collapsed){
38164 this.updateBody(box.width, null);
38166 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38170 Roo.bootstrap.layout.East = function(config){
38171 config.region = "east";
38172 config.cursor = "e-resize";
38173 Roo.bootstrap.layout.Split.call(this, config);
38175 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38176 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38177 this.split.el.addClass("roo-layout-split-h");
38179 var size = config.initialSize || config.width;
38180 if(typeof size != "undefined"){
38181 this.el.setWidth(size);
38184 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38185 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38186 getBox : function(){
38187 if(this.collapsed){
38188 return this.collapsedEl.getBox();
38190 var box = this.el.getBox();
38192 var sw = this.split.el.getWidth();
38199 updateBox : function(box){
38200 if(this.split && !this.collapsed){
38201 var sw = this.split.el.getWidth();
38203 this.split.el.setLeft(box.x);
38204 this.split.el.setTop(box.y);
38205 this.split.el.setHeight(box.height);
38208 if(this.collapsed){
38209 this.updateBody(null, box.height);
38211 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38215 Roo.bootstrap.layout.West = function(config){
38216 config.region = "west";
38217 config.cursor = "w-resize";
38219 Roo.bootstrap.layout.Split.call(this, config);
38221 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38222 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38223 this.split.el.addClass("roo-layout-split-h");
38227 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38228 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38230 onRender: function(ctr, pos)
38232 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38233 var size = this.config.initialSize || this.config.width;
38234 if(typeof size != "undefined"){
38235 this.el.setWidth(size);
38239 getBox : function(){
38240 if(this.collapsed){
38241 return this.collapsedEl.getBox();
38243 var box = this.el.getBox();
38245 box.width += this.split.el.getWidth();
38250 updateBox : function(box){
38251 if(this.split && !this.collapsed){
38252 var sw = this.split.el.getWidth();
38254 this.split.el.setLeft(box.x+box.width);
38255 this.split.el.setTop(box.y);
38256 this.split.el.setHeight(box.height);
38258 if(this.collapsed){
38259 this.updateBody(null, box.height);
38261 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38263 });Roo.namespace("Roo.bootstrap.panel");/*
38265 * Ext JS Library 1.1.1
38266 * Copyright(c) 2006-2007, Ext JS, LLC.
38268 * Originally Released Under LGPL - original licence link has changed is not relivant.
38271 * <script type="text/javascript">
38274 * @class Roo.ContentPanel
38275 * @extends Roo.util.Observable
38276 * A basic ContentPanel element.
38277 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38278 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38279 * @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
38280 * @cfg {Boolean} closable True if the panel can be closed/removed
38281 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38282 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38283 * @cfg {Toolbar} toolbar A toolbar for this panel
38284 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38285 * @cfg {String} title The title for this panel
38286 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38287 * @cfg {String} url Calls {@link #setUrl} with this value
38288 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38289 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38290 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38291 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38292 * @cfg {Boolean} badges render the badges
38295 * Create a new ContentPanel.
38296 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38297 * @param {String/Object} config A string to set only the title or a config object
38298 * @param {String} content (optional) Set the HTML content for this panel
38299 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38301 Roo.bootstrap.panel.Content = function( config){
38303 this.tpl = config.tpl || false;
38305 var el = config.el;
38306 var content = config.content;
38308 if(config.autoCreate){ // xtype is available if this is called from factory
38311 this.el = Roo.get(el);
38312 if(!this.el && config && config.autoCreate){
38313 if(typeof config.autoCreate == "object"){
38314 if(!config.autoCreate.id){
38315 config.autoCreate.id = config.id||el;
38317 this.el = Roo.DomHelper.append(document.body,
38318 config.autoCreate, true);
38320 var elcfg = { tag: "div",
38321 cls: "roo-layout-inactive-content",
38325 elcfg.html = config.html;
38329 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38332 this.closable = false;
38333 this.loaded = false;
38334 this.active = false;
38337 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38339 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38341 this.wrapEl = this.el; //this.el.wrap();
38343 if (config.toolbar.items) {
38344 ti = config.toolbar.items ;
38345 delete config.toolbar.items ;
38349 this.toolbar.render(this.wrapEl, 'before');
38350 for(var i =0;i < ti.length;i++) {
38351 // Roo.log(['add child', items[i]]);
38352 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38354 this.toolbar.items = nitems;
38355 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38356 delete config.toolbar;
38360 // xtype created footer. - not sure if will work as we normally have to render first..
38361 if (this.footer && !this.footer.el && this.footer.xtype) {
38362 if (!this.wrapEl) {
38363 this.wrapEl = this.el.wrap();
38366 this.footer.container = this.wrapEl.createChild();
38368 this.footer = Roo.factory(this.footer, Roo);
38373 if(typeof config == "string"){
38374 this.title = config;
38376 Roo.apply(this, config);
38380 this.resizeEl = Roo.get(this.resizeEl, true);
38382 this.resizeEl = this.el;
38384 // handle view.xtype
38392 * Fires when this panel is activated.
38393 * @param {Roo.ContentPanel} this
38397 * @event deactivate
38398 * Fires when this panel is activated.
38399 * @param {Roo.ContentPanel} this
38401 "deactivate" : true,
38405 * Fires when this panel is resized if fitToFrame is true.
38406 * @param {Roo.ContentPanel} this
38407 * @param {Number} width The width after any component adjustments
38408 * @param {Number} height The height after any component adjustments
38414 * Fires when this tab is created
38415 * @param {Roo.ContentPanel} this
38426 if(this.autoScroll){
38427 this.resizeEl.setStyle("overflow", "auto");
38429 // fix randome scrolling
38430 //this.el.on('scroll', function() {
38431 // Roo.log('fix random scolling');
38432 // this.scrollTo('top',0);
38435 content = content || this.content;
38437 this.setContent(content);
38439 if(config && config.url){
38440 this.setUrl(this.url, this.params, this.loadOnce);
38445 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38447 if (this.view && typeof(this.view.xtype) != 'undefined') {
38448 this.view.el = this.el.appendChild(document.createElement("div"));
38449 this.view = Roo.factory(this.view);
38450 this.view.render && this.view.render(false, '');
38454 this.fireEvent('render', this);
38457 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38461 setRegion : function(region){
38462 this.region = region;
38463 this.setActiveClass(region && !this.background);
38467 setActiveClass: function(state)
38470 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38471 this.el.setStyle('position','relative');
38473 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38474 this.el.setStyle('position', 'absolute');
38479 * Returns the toolbar for this Panel if one was configured.
38480 * @return {Roo.Toolbar}
38482 getToolbar : function(){
38483 return this.toolbar;
38486 setActiveState : function(active)
38488 this.active = active;
38489 this.setActiveClass(active);
38491 if(this.fireEvent("deactivate", this) === false){
38496 this.fireEvent("activate", this);
38500 * Updates this panel's element
38501 * @param {String} content The new content
38502 * @param {Boolean} loadScripts (optional) true to look for and process scripts
38504 setContent : function(content, loadScripts){
38505 this.el.update(content, loadScripts);
38508 ignoreResize : function(w, h){
38509 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38512 this.lastSize = {width: w, height: h};
38517 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38518 * @return {Roo.UpdateManager} The UpdateManager
38520 getUpdateManager : function(){
38521 return this.el.getUpdateManager();
38524 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38525 * @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:
38528 url: "your-url.php",
38529 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38530 callback: yourFunction,
38531 scope: yourObject, //(optional scope)
38534 text: "Loading...",
38539 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38540 * 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.
38541 * @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}
38542 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38543 * @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.
38544 * @return {Roo.ContentPanel} this
38547 var um = this.el.getUpdateManager();
38548 um.update.apply(um, arguments);
38554 * 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.
38555 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38556 * @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)
38557 * @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)
38558 * @return {Roo.UpdateManager} The UpdateManager
38560 setUrl : function(url, params, loadOnce){
38561 if(this.refreshDelegate){
38562 this.removeListener("activate", this.refreshDelegate);
38564 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38565 this.on("activate", this.refreshDelegate);
38566 return this.el.getUpdateManager();
38569 _handleRefresh : function(url, params, loadOnce){
38570 if(!loadOnce || !this.loaded){
38571 var updater = this.el.getUpdateManager();
38572 updater.update(url, params, this._setLoaded.createDelegate(this));
38576 _setLoaded : function(){
38577 this.loaded = true;
38581 * Returns this panel's id
38584 getId : function(){
38589 * Returns this panel's element - used by regiosn to add.
38590 * @return {Roo.Element}
38592 getEl : function(){
38593 return this.wrapEl || this.el;
38598 adjustForComponents : function(width, height)
38600 //Roo.log('adjustForComponents ');
38601 if(this.resizeEl != this.el){
38602 width -= this.el.getFrameWidth('lr');
38603 height -= this.el.getFrameWidth('tb');
38606 var te = this.toolbar.getEl();
38607 te.setWidth(width);
38608 height -= te.getHeight();
38611 var te = this.footer.getEl();
38612 te.setWidth(width);
38613 height -= te.getHeight();
38617 if(this.adjustments){
38618 width += this.adjustments[0];
38619 height += this.adjustments[1];
38621 return {"width": width, "height": height};
38624 setSize : function(width, height){
38625 if(this.fitToFrame && !this.ignoreResize(width, height)){
38626 if(this.fitContainer && this.resizeEl != this.el){
38627 this.el.setSize(width, height);
38629 var size = this.adjustForComponents(width, height);
38630 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38631 this.fireEvent('resize', this, size.width, size.height);
38636 * Returns this panel's title
38639 getTitle : function(){
38641 if (typeof(this.title) != 'object') {
38646 for (var k in this.title) {
38647 if (!this.title.hasOwnProperty(k)) {
38651 if (k.indexOf('-') >= 0) {
38652 var s = k.split('-');
38653 for (var i = 0; i<s.length; i++) {
38654 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38657 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38664 * Set this panel's title
38665 * @param {String} title
38667 setTitle : function(title){
38668 this.title = title;
38670 this.region.updatePanelTitle(this, title);
38675 * Returns true is this panel was configured to be closable
38676 * @return {Boolean}
38678 isClosable : function(){
38679 return this.closable;
38682 beforeSlide : function(){
38684 this.resizeEl.clip();
38687 afterSlide : function(){
38689 this.resizeEl.unclip();
38693 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38694 * Will fail silently if the {@link #setUrl} method has not been called.
38695 * This does not activate the panel, just updates its content.
38697 refresh : function(){
38698 if(this.refreshDelegate){
38699 this.loaded = false;
38700 this.refreshDelegate();
38705 * Destroys this panel
38707 destroy : function(){
38708 this.el.removeAllListeners();
38709 var tempEl = document.createElement("span");
38710 tempEl.appendChild(this.el.dom);
38711 tempEl.innerHTML = "";
38717 * form - if the content panel contains a form - this is a reference to it.
38718 * @type {Roo.form.Form}
38722 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38723 * This contains a reference to it.
38729 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38739 * @param {Object} cfg Xtype definition of item to add.
38743 getChildContainer: function () {
38744 return this.getEl();
38749 var ret = new Roo.factory(cfg);
38754 if (cfg.xtype.match(/^Form$/)) {
38757 //if (this.footer) {
38758 // el = this.footer.container.insertSibling(false, 'before');
38760 el = this.el.createChild();
38763 this.form = new Roo.form.Form(cfg);
38766 if ( this.form.allItems.length) {
38767 this.form.render(el.dom);
38771 // should only have one of theses..
38772 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38773 // views.. should not be just added - used named prop 'view''
38775 cfg.el = this.el.appendChild(document.createElement("div"));
38778 var ret = new Roo.factory(cfg);
38780 ret.render && ret.render(false, ''); // render blank..
38790 * @class Roo.bootstrap.panel.Grid
38791 * @extends Roo.bootstrap.panel.Content
38793 * Create a new GridPanel.
38794 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38795 * @param {Object} config A the config object
38801 Roo.bootstrap.panel.Grid = function(config)
38805 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38806 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38808 config.el = this.wrapper;
38809 //this.el = this.wrapper;
38811 if (config.container) {
38812 // ctor'ed from a Border/panel.grid
38815 this.wrapper.setStyle("overflow", "hidden");
38816 this.wrapper.addClass('roo-grid-container');
38821 if(config.toolbar){
38822 var tool_el = this.wrapper.createChild();
38823 this.toolbar = Roo.factory(config.toolbar);
38825 if (config.toolbar.items) {
38826 ti = config.toolbar.items ;
38827 delete config.toolbar.items ;
38831 this.toolbar.render(tool_el);
38832 for(var i =0;i < ti.length;i++) {
38833 // Roo.log(['add child', items[i]]);
38834 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38836 this.toolbar.items = nitems;
38838 delete config.toolbar;
38841 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38842 config.grid.scrollBody = true;;
38843 config.grid.monitorWindowResize = false; // turn off autosizing
38844 config.grid.autoHeight = false;
38845 config.grid.autoWidth = false;
38847 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38849 if (config.background) {
38850 // render grid on panel activation (if panel background)
38851 this.on('activate', function(gp) {
38852 if (!gp.grid.rendered) {
38853 gp.grid.render(this.wrapper);
38854 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38859 this.grid.render(this.wrapper);
38860 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38863 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38864 // ??? needed ??? config.el = this.wrapper;
38869 // xtype created footer. - not sure if will work as we normally have to render first..
38870 if (this.footer && !this.footer.el && this.footer.xtype) {
38872 var ctr = this.grid.getView().getFooterPanel(true);
38873 this.footer.dataSource = this.grid.dataSource;
38874 this.footer = Roo.factory(this.footer, Roo);
38875 this.footer.render(ctr);
38885 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38886 getId : function(){
38887 return this.grid.id;
38891 * Returns the grid for this panel
38892 * @return {Roo.bootstrap.Table}
38894 getGrid : function(){
38898 setSize : function(width, height){
38899 if(!this.ignoreResize(width, height)){
38900 var grid = this.grid;
38901 var size = this.adjustForComponents(width, height);
38902 var gridel = grid.getGridEl();
38903 gridel.setSize(size.width, size.height);
38905 var thd = grid.getGridEl().select('thead',true).first();
38906 var tbd = grid.getGridEl().select('tbody', true).first();
38908 tbd.setSize(width, height - thd.getHeight());
38917 beforeSlide : function(){
38918 this.grid.getView().scroller.clip();
38921 afterSlide : function(){
38922 this.grid.getView().scroller.unclip();
38925 destroy : function(){
38926 this.grid.destroy();
38928 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38933 * @class Roo.bootstrap.panel.Nest
38934 * @extends Roo.bootstrap.panel.Content
38936 * Create a new Panel, that can contain a layout.Border.
38939 * @param {Roo.BorderLayout} layout The layout for this panel
38940 * @param {String/Object} config A string to set only the title or a config object
38942 Roo.bootstrap.panel.Nest = function(config)
38944 // construct with only one argument..
38945 /* FIXME - implement nicer consturctors
38946 if (layout.layout) {
38948 layout = config.layout;
38949 delete config.layout;
38951 if (layout.xtype && !layout.getEl) {
38952 // then layout needs constructing..
38953 layout = Roo.factory(layout, Roo);
38957 config.el = config.layout.getEl();
38959 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38961 config.layout.monitorWindowResize = false; // turn off autosizing
38962 this.layout = config.layout;
38963 this.layout.getEl().addClass("roo-layout-nested-layout");
38964 this.layout.parent = this;
38971 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38973 setSize : function(width, height){
38974 if(!this.ignoreResize(width, height)){
38975 var size = this.adjustForComponents(width, height);
38976 var el = this.layout.getEl();
38977 if (size.height < 1) {
38978 el.setWidth(size.width);
38980 el.setSize(size.width, size.height);
38982 var touch = el.dom.offsetWidth;
38983 this.layout.layout();
38984 // ie requires a double layout on the first pass
38985 if(Roo.isIE && !this.initialized){
38986 this.initialized = true;
38987 this.layout.layout();
38992 // activate all subpanels if not currently active..
38994 setActiveState : function(active){
38995 this.active = active;
38996 this.setActiveClass(active);
38999 this.fireEvent("deactivate", this);
39003 this.fireEvent("activate", this);
39004 // not sure if this should happen before or after..
39005 if (!this.layout) {
39006 return; // should not happen..
39009 for (var r in this.layout.regions) {
39010 reg = this.layout.getRegion(r);
39011 if (reg.getActivePanel()) {
39012 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39013 reg.setActivePanel(reg.getActivePanel());
39016 if (!reg.panels.length) {
39019 reg.showPanel(reg.getPanel(0));
39028 * Returns the nested BorderLayout for this panel
39029 * @return {Roo.BorderLayout}
39031 getLayout : function(){
39032 return this.layout;
39036 * Adds a xtype elements to the layout of the nested panel
39040 xtype : 'ContentPanel',
39047 xtype : 'NestedLayoutPanel',
39053 items : [ ... list of content panels or nested layout panels.. ]
39057 * @param {Object} cfg Xtype definition of item to add.
39059 addxtype : function(cfg) {
39060 return this.layout.addxtype(cfg);
39065 * Ext JS Library 1.1.1
39066 * Copyright(c) 2006-2007, Ext JS, LLC.
39068 * Originally Released Under LGPL - original licence link has changed is not relivant.
39071 * <script type="text/javascript">
39074 * @class Roo.TabPanel
39075 * @extends Roo.util.Observable
39076 * A lightweight tab container.
39080 // basic tabs 1, built from existing content
39081 var tabs = new Roo.TabPanel("tabs1");
39082 tabs.addTab("script", "View Script");
39083 tabs.addTab("markup", "View Markup");
39084 tabs.activate("script");
39086 // more advanced tabs, built from javascript
39087 var jtabs = new Roo.TabPanel("jtabs");
39088 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39090 // set up the UpdateManager
39091 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39092 var updater = tab2.getUpdateManager();
39093 updater.setDefaultUrl("ajax1.htm");
39094 tab2.on('activate', updater.refresh, updater, true);
39096 // Use setUrl for Ajax loading
39097 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39098 tab3.setUrl("ajax2.htm", null, true);
39101 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39104 jtabs.activate("jtabs-1");
39107 * Create a new TabPanel.
39108 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39109 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39111 Roo.bootstrap.panel.Tabs = function(config){
39113 * The container element for this TabPanel.
39114 * @type Roo.Element
39116 this.el = Roo.get(config.el);
39119 if(typeof config == "boolean"){
39120 this.tabPosition = config ? "bottom" : "top";
39122 Roo.apply(this, config);
39126 if(this.tabPosition == "bottom"){
39127 // if tabs are at the bottom = create the body first.
39128 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39129 this.el.addClass("roo-tabs-bottom");
39131 // next create the tabs holders
39133 if (this.tabPosition == "west"){
39135 var reg = this.region; // fake it..
39137 if (!reg.mgr.parent) {
39140 reg = reg.mgr.parent.region;
39142 Roo.log("got nest?");
39144 if (reg.mgr.getRegion('west')) {
39145 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39146 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39147 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39148 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39149 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39157 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39158 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39159 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39160 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39165 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39168 // finally - if tabs are at the top, then create the body last..
39169 if(this.tabPosition != "bottom"){
39170 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39171 * @type Roo.Element
39173 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39174 this.el.addClass("roo-tabs-top");
39178 this.bodyEl.setStyle("position", "relative");
39180 this.active = null;
39181 this.activateDelegate = this.activate.createDelegate(this);
39186 * Fires when the active tab changes
39187 * @param {Roo.TabPanel} this
39188 * @param {Roo.TabPanelItem} activePanel The new active tab
39192 * @event beforetabchange
39193 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39194 * @param {Roo.TabPanel} this
39195 * @param {Object} e Set cancel to true on this object to cancel the tab change
39196 * @param {Roo.TabPanelItem} tab The tab being changed to
39198 "beforetabchange" : true
39201 Roo.EventManager.onWindowResize(this.onResize, this);
39202 this.cpad = this.el.getPadding("lr");
39203 this.hiddenCount = 0;
39206 // toolbar on the tabbar support...
39207 if (this.toolbar) {
39208 alert("no toolbar support yet");
39209 this.toolbar = false;
39211 var tcfg = this.toolbar;
39212 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39213 this.toolbar = new Roo.Toolbar(tcfg);
39214 if (Roo.isSafari) {
39215 var tbl = tcfg.container.child('table', true);
39216 tbl.setAttribute('width', '100%');
39224 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39227 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39229 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39231 tabPosition : "top",
39233 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39235 currentTabWidth : 0,
39237 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39241 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39245 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39247 preferredTabWidth : 175,
39249 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39251 resizeTabs : false,
39253 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39255 monitorResize : true,
39257 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39259 toolbar : false, // set by caller..
39261 region : false, /// set by caller
39263 disableTooltips : true, // not used yet...
39266 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39267 * @param {String} id The id of the div to use <b>or create</b>
39268 * @param {String} text The text for the tab
39269 * @param {String} content (optional) Content to put in the TabPanelItem body
39270 * @param {Boolean} closable (optional) True to create a close icon on the tab
39271 * @return {Roo.TabPanelItem} The created TabPanelItem
39273 addTab : function(id, text, content, closable, tpl)
39275 var item = new Roo.bootstrap.panel.TabItem({
39279 closable : closable,
39282 this.addTabItem(item);
39284 item.setContent(content);
39290 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39291 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39292 * @return {Roo.TabPanelItem}
39294 getTab : function(id){
39295 return this.items[id];
39299 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39300 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39302 hideTab : function(id){
39303 var t = this.items[id];
39306 this.hiddenCount++;
39307 this.autoSizeTabs();
39312 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39313 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39315 unhideTab : function(id){
39316 var t = this.items[id];
39318 t.setHidden(false);
39319 this.hiddenCount--;
39320 this.autoSizeTabs();
39325 * Adds an existing {@link Roo.TabPanelItem}.
39326 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39328 addTabItem : function(item)
39330 this.items[item.id] = item;
39331 this.items.push(item);
39332 this.autoSizeTabs();
39333 // if(this.resizeTabs){
39334 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39335 // this.autoSizeTabs();
39337 // item.autoSize();
39342 * Removes a {@link Roo.TabPanelItem}.
39343 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39345 removeTab : function(id){
39346 var items = this.items;
39347 var tab = items[id];
39348 if(!tab) { return; }
39349 var index = items.indexOf(tab);
39350 if(this.active == tab && items.length > 1){
39351 var newTab = this.getNextAvailable(index);
39356 this.stripEl.dom.removeChild(tab.pnode.dom);
39357 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39358 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39360 items.splice(index, 1);
39361 delete this.items[tab.id];
39362 tab.fireEvent("close", tab);
39363 tab.purgeListeners();
39364 this.autoSizeTabs();
39367 getNextAvailable : function(start){
39368 var items = this.items;
39370 // look for a next tab that will slide over to
39371 // replace the one being removed
39372 while(index < items.length){
39373 var item = items[++index];
39374 if(item && !item.isHidden()){
39378 // if one isn't found select the previous tab (on the left)
39381 var item = items[--index];
39382 if(item && !item.isHidden()){
39390 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39391 * @param {String/Number} id The id or index of the TabPanelItem to disable.
39393 disableTab : function(id){
39394 var tab = this.items[id];
39395 if(tab && this.active != tab){
39401 * Enables a {@link Roo.TabPanelItem} that is disabled.
39402 * @param {String/Number} id The id or index of the TabPanelItem to enable.
39404 enableTab : function(id){
39405 var tab = this.items[id];
39410 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39411 * @param {String/Number} id The id or index of the TabPanelItem to activate.
39412 * @return {Roo.TabPanelItem} The TabPanelItem.
39414 activate : function(id)
39416 //Roo.log('activite:' + id);
39418 var tab = this.items[id];
39422 if(tab == this.active || tab.disabled){
39426 this.fireEvent("beforetabchange", this, e, tab);
39427 if(e.cancel !== true && !tab.disabled){
39429 this.active.hide();
39431 this.active = this.items[id];
39432 this.active.show();
39433 this.fireEvent("tabchange", this, this.active);
39439 * Gets the active {@link Roo.TabPanelItem}.
39440 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39442 getActiveTab : function(){
39443 return this.active;
39447 * Updates the tab body element to fit the height of the container element
39448 * for overflow scrolling
39449 * @param {Number} targetHeight (optional) Override the starting height from the elements height
39451 syncHeight : function(targetHeight){
39452 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39453 var bm = this.bodyEl.getMargins();
39454 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39455 this.bodyEl.setHeight(newHeight);
39459 onResize : function(){
39460 if(this.monitorResize){
39461 this.autoSizeTabs();
39466 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39468 beginUpdate : function(){
39469 this.updating = true;
39473 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39475 endUpdate : function(){
39476 this.updating = false;
39477 this.autoSizeTabs();
39481 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39483 autoSizeTabs : function()
39485 var count = this.items.length;
39486 var vcount = count - this.hiddenCount;
39489 this.stripEl.hide();
39491 this.stripEl.show();
39494 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39499 var w = Math.max(this.el.getWidth() - this.cpad, 10);
39500 var availWidth = Math.floor(w / vcount);
39501 var b = this.stripBody;
39502 if(b.getWidth() > w){
39503 var tabs = this.items;
39504 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39505 if(availWidth < this.minTabWidth){
39506 /*if(!this.sleft){ // incomplete scrolling code
39507 this.createScrollButtons();
39510 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39513 if(this.currentTabWidth < this.preferredTabWidth){
39514 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39520 * Returns the number of tabs in this TabPanel.
39523 getCount : function(){
39524 return this.items.length;
39528 * Resizes all the tabs to the passed width
39529 * @param {Number} The new width
39531 setTabWidth : function(width){
39532 this.currentTabWidth = width;
39533 for(var i = 0, len = this.items.length; i < len; i++) {
39534 if(!this.items[i].isHidden()) {
39535 this.items[i].setWidth(width);
39541 * Destroys this TabPanel
39542 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39544 destroy : function(removeEl){
39545 Roo.EventManager.removeResizeListener(this.onResize, this);
39546 for(var i = 0, len = this.items.length; i < len; i++){
39547 this.items[i].purgeListeners();
39549 if(removeEl === true){
39550 this.el.update("");
39555 createStrip : function(container)
39557 var strip = document.createElement("nav");
39558 strip.className = Roo.bootstrap.version == 4 ?
39559 "navbar-light bg-light" :
39560 "navbar navbar-default"; //"x-tabs-wrap";
39561 container.appendChild(strip);
39565 createStripList : function(strip)
39567 // div wrapper for retard IE
39568 // returns the "tr" element.
39569 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39570 //'<div class="x-tabs-strip-wrap">'+
39571 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39572 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39573 return strip.firstChild; //.firstChild.firstChild.firstChild;
39575 createBody : function(container)
39577 var body = document.createElement("div");
39578 Roo.id(body, "tab-body");
39579 //Roo.fly(body).addClass("x-tabs-body");
39580 Roo.fly(body).addClass("tab-content");
39581 container.appendChild(body);
39584 createItemBody :function(bodyEl, id){
39585 var body = Roo.getDom(id);
39587 body = document.createElement("div");
39590 //Roo.fly(body).addClass("x-tabs-item-body");
39591 Roo.fly(body).addClass("tab-pane");
39592 bodyEl.insertBefore(body, bodyEl.firstChild);
39596 createStripElements : function(stripEl, text, closable, tpl)
39598 var td = document.createElement("li"); // was td..
39599 td.className = 'nav-item';
39601 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39604 stripEl.appendChild(td);
39606 td.className = "x-tabs-closable";
39607 if(!this.closeTpl){
39608 this.closeTpl = new Roo.Template(
39609 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39610 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39611 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39614 var el = this.closeTpl.overwrite(td, {"text": text});
39615 var close = el.getElementsByTagName("div")[0];
39616 var inner = el.getElementsByTagName("em")[0];
39617 return {"el": el, "close": close, "inner": inner};
39620 // not sure what this is..
39621 // if(!this.tabTpl){
39622 //this.tabTpl = new Roo.Template(
39623 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39624 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39626 // this.tabTpl = new Roo.Template(
39627 // '<a href="#">' +
39628 // '<span unselectable="on"' +
39629 // (this.disableTooltips ? '' : ' title="{text}"') +
39630 // ' >{text}</span></a>'
39636 var template = tpl || this.tabTpl || false;
39639 template = new Roo.Template(
39640 Roo.bootstrap.version == 4 ?
39642 '<a class="nav-link" href="#" unselectable="on"' +
39643 (this.disableTooltips ? '' : ' title="{text}"') +
39646 '<a class="nav-link" href="#">' +
39647 '<span unselectable="on"' +
39648 (this.disableTooltips ? '' : ' title="{text}"') +
39649 ' >{text}</span></a>'
39654 switch (typeof(template)) {
39658 template = new Roo.Template(template);
39664 var el = template.overwrite(td, {"text": text});
39666 var inner = el.getElementsByTagName("span")[0];
39668 return {"el": el, "inner": inner};
39676 * @class Roo.TabPanelItem
39677 * @extends Roo.util.Observable
39678 * Represents an individual item (tab plus body) in a TabPanel.
39679 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39680 * @param {String} id The id of this TabPanelItem
39681 * @param {String} text The text for the tab of this TabPanelItem
39682 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39684 Roo.bootstrap.panel.TabItem = function(config){
39686 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39687 * @type Roo.TabPanel
39689 this.tabPanel = config.panel;
39691 * The id for this TabPanelItem
39694 this.id = config.id;
39696 this.disabled = false;
39698 this.text = config.text;
39700 this.loaded = false;
39701 this.closable = config.closable;
39704 * The body element for this TabPanelItem.
39705 * @type Roo.Element
39707 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39708 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39709 this.bodyEl.setStyle("display", "block");
39710 this.bodyEl.setStyle("zoom", "1");
39711 //this.hideAction();
39713 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39715 this.el = Roo.get(els.el);
39716 this.inner = Roo.get(els.inner, true);
39717 this.textEl = Roo.bootstrap.version == 4 ?
39718 this.el : Roo.get(this.el.dom.firstChild, true);
39720 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39721 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39724 // this.el.on("mousedown", this.onTabMouseDown, this);
39725 this.el.on("click", this.onTabClick, this);
39727 if(config.closable){
39728 var c = Roo.get(els.close, true);
39729 c.dom.title = this.closeText;
39730 c.addClassOnOver("close-over");
39731 c.on("click", this.closeClick, this);
39737 * Fires when this tab becomes the active tab.
39738 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39739 * @param {Roo.TabPanelItem} this
39743 * @event beforeclose
39744 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39745 * @param {Roo.TabPanelItem} this
39746 * @param {Object} e Set cancel to true on this object to cancel the close.
39748 "beforeclose": true,
39751 * Fires when this tab is closed.
39752 * @param {Roo.TabPanelItem} this
39756 * @event deactivate
39757 * Fires when this tab is no longer the active tab.
39758 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39759 * @param {Roo.TabPanelItem} this
39761 "deactivate" : true
39763 this.hidden = false;
39765 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39768 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39770 purgeListeners : function(){
39771 Roo.util.Observable.prototype.purgeListeners.call(this);
39772 this.el.removeAllListeners();
39775 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39778 this.status_node.addClass("active");
39781 this.tabPanel.stripWrap.repaint();
39783 this.fireEvent("activate", this.tabPanel, this);
39787 * Returns true if this tab is the active tab.
39788 * @return {Boolean}
39790 isActive : function(){
39791 return this.tabPanel.getActiveTab() == this;
39795 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39798 this.status_node.removeClass("active");
39800 this.fireEvent("deactivate", this.tabPanel, this);
39803 hideAction : function(){
39804 this.bodyEl.hide();
39805 this.bodyEl.setStyle("position", "absolute");
39806 this.bodyEl.setLeft("-20000px");
39807 this.bodyEl.setTop("-20000px");
39810 showAction : function(){
39811 this.bodyEl.setStyle("position", "relative");
39812 this.bodyEl.setTop("");
39813 this.bodyEl.setLeft("");
39814 this.bodyEl.show();
39818 * Set the tooltip for the tab.
39819 * @param {String} tooltip The tab's tooltip
39821 setTooltip : function(text){
39822 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39823 this.textEl.dom.qtip = text;
39824 this.textEl.dom.removeAttribute('title');
39826 this.textEl.dom.title = text;
39830 onTabClick : function(e){
39831 e.preventDefault();
39832 this.tabPanel.activate(this.id);
39835 onTabMouseDown : function(e){
39836 e.preventDefault();
39837 this.tabPanel.activate(this.id);
39840 getWidth : function(){
39841 return this.inner.getWidth();
39844 setWidth : function(width){
39845 var iwidth = width - this.linode.getPadding("lr");
39846 this.inner.setWidth(iwidth);
39847 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39848 this.linode.setWidth(width);
39852 * Show or hide the tab
39853 * @param {Boolean} hidden True to hide or false to show.
39855 setHidden : function(hidden){
39856 this.hidden = hidden;
39857 this.linode.setStyle("display", hidden ? "none" : "");
39861 * Returns true if this tab is "hidden"
39862 * @return {Boolean}
39864 isHidden : function(){
39865 return this.hidden;
39869 * Returns the text for this tab
39872 getText : function(){
39876 autoSize : function(){
39877 //this.el.beginMeasure();
39878 this.textEl.setWidth(1);
39880 * #2804 [new] Tabs in Roojs
39881 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39883 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39884 //this.el.endMeasure();
39888 * Sets the text for the tab (Note: this also sets the tooltip text)
39889 * @param {String} text The tab's text and tooltip
39891 setText : function(text){
39893 this.textEl.update(text);
39894 this.setTooltip(text);
39895 //if(!this.tabPanel.resizeTabs){
39896 // this.autoSize();
39900 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39902 activate : function(){
39903 this.tabPanel.activate(this.id);
39907 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39909 disable : function(){
39910 if(this.tabPanel.active != this){
39911 this.disabled = true;
39912 this.status_node.addClass("disabled");
39917 * Enables this TabPanelItem if it was previously disabled.
39919 enable : function(){
39920 this.disabled = false;
39921 this.status_node.removeClass("disabled");
39925 * Sets the content for this TabPanelItem.
39926 * @param {String} content The content
39927 * @param {Boolean} loadScripts true to look for and load scripts
39929 setContent : function(content, loadScripts){
39930 this.bodyEl.update(content, loadScripts);
39934 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39935 * @return {Roo.UpdateManager} The UpdateManager
39937 getUpdateManager : function(){
39938 return this.bodyEl.getUpdateManager();
39942 * Set a URL to be used to load the content for this TabPanelItem.
39943 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39944 * @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)
39945 * @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)
39946 * @return {Roo.UpdateManager} The UpdateManager
39948 setUrl : function(url, params, loadOnce){
39949 if(this.refreshDelegate){
39950 this.un('activate', this.refreshDelegate);
39952 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39953 this.on("activate", this.refreshDelegate);
39954 return this.bodyEl.getUpdateManager();
39958 _handleRefresh : function(url, params, loadOnce){
39959 if(!loadOnce || !this.loaded){
39960 var updater = this.bodyEl.getUpdateManager();
39961 updater.update(url, params, this._setLoaded.createDelegate(this));
39966 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39967 * Will fail silently if the setUrl method has not been called.
39968 * This does not activate the panel, just updates its content.
39970 refresh : function(){
39971 if(this.refreshDelegate){
39972 this.loaded = false;
39973 this.refreshDelegate();
39978 _setLoaded : function(){
39979 this.loaded = true;
39983 closeClick : function(e){
39986 this.fireEvent("beforeclose", this, o);
39987 if(o.cancel !== true){
39988 this.tabPanel.removeTab(this.id);
39992 * The text displayed in the tooltip for the close icon.
39995 closeText : "Close this tab"
39998 * This script refer to:
39999 * Title: International Telephone Input
40000 * Author: Jack O'Connor
40001 * Code version: v12.1.12
40002 * Availability: https://github.com/jackocnr/intl-tel-input.git
40005 Roo.bootstrap.PhoneInputData = function() {
40008 "Afghanistan (افغانستان)",
40013 "Albania (Shqipëri)",
40018 "Algeria (الجزائر)",
40043 "Antigua and Barbuda",
40053 "Armenia (Հայաստան)",
40069 "Austria (Österreich)",
40074 "Azerbaijan (Azərbaycan)",
40084 "Bahrain (البحرين)",
40089 "Bangladesh (বাংলাদেশ)",
40099 "Belarus (Беларусь)",
40104 "Belgium (België)",
40134 "Bosnia and Herzegovina (Босна и Херцеговина)",
40149 "British Indian Ocean Territory",
40154 "British Virgin Islands",
40164 "Bulgaria (България)",
40174 "Burundi (Uburundi)",
40179 "Cambodia (កម្ពុជា)",
40184 "Cameroon (Cameroun)",
40193 ["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"]
40196 "Cape Verde (Kabu Verdi)",
40201 "Caribbean Netherlands",
40212 "Central African Republic (République centrafricaine)",
40232 "Christmas Island",
40238 "Cocos (Keeling) Islands",
40249 "Comoros (جزر القمر)",
40254 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40259 "Congo (Republic) (Congo-Brazzaville)",
40279 "Croatia (Hrvatska)",
40300 "Czech Republic (Česká republika)",
40305 "Denmark (Danmark)",
40320 "Dominican Republic (República Dominicana)",
40324 ["809", "829", "849"]
40342 "Equatorial Guinea (Guinea Ecuatorial)",
40362 "Falkland Islands (Islas Malvinas)",
40367 "Faroe Islands (Føroyar)",
40388 "French Guiana (Guyane française)",
40393 "French Polynesia (Polynésie française)",
40408 "Georgia (საქართველო)",
40413 "Germany (Deutschland)",
40433 "Greenland (Kalaallit Nunaat)",
40470 "Guinea-Bissau (Guiné Bissau)",
40495 "Hungary (Magyarország)",
40500 "Iceland (Ísland)",
40520 "Iraq (العراق)",
40536 "Israel (ישראל)",
40563 "Jordan (الأردن)",
40568 "Kazakhstan (Казахстан)",
40589 "Kuwait (الكويت)",
40594 "Kyrgyzstan (Кыргызстан)",
40604 "Latvia (Latvija)",
40609 "Lebanon (لبنان)",
40624 "Libya (ليبيا)",
40634 "Lithuania (Lietuva)",
40649 "Macedonia (FYROM) (Македонија)",
40654 "Madagascar (Madagasikara)",
40684 "Marshall Islands",
40694 "Mauritania (موريتانيا)",
40699 "Mauritius (Moris)",
40720 "Moldova (Republica Moldova)",
40730 "Mongolia (Монгол)",
40735 "Montenegro (Crna Gora)",
40745 "Morocco (المغرب)",
40751 "Mozambique (Moçambique)",
40756 "Myanmar (Burma) (မြန်မာ)",
40761 "Namibia (Namibië)",
40776 "Netherlands (Nederland)",
40781 "New Caledonia (Nouvelle-Calédonie)",
40816 "North Korea (조선 민주주의 인민 공화국)",
40821 "Northern Mariana Islands",
40837 "Pakistan (پاکستان)",
40847 "Palestine (فلسطين)",
40857 "Papua New Guinea",
40899 "Réunion (La Réunion)",
40905 "Romania (România)",
40921 "Saint Barthélemy",
40932 "Saint Kitts and Nevis",
40942 "Saint Martin (Saint-Martin (partie française))",
40948 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40953 "Saint Vincent and the Grenadines",
40968 "São Tomé and Príncipe (São Tomé e Príncipe)",
40973 "Saudi Arabia (المملكة العربية السعودية)",
40978 "Senegal (Sénégal)",
41008 "Slovakia (Slovensko)",
41013 "Slovenia (Slovenija)",
41023 "Somalia (Soomaaliya)",
41033 "South Korea (대한민국)",
41038 "South Sudan (جنوب السودان)",
41048 "Sri Lanka (ශ්රී ලංකාව)",
41053 "Sudan (السودان)",
41063 "Svalbard and Jan Mayen",
41074 "Sweden (Sverige)",
41079 "Switzerland (Schweiz)",
41084 "Syria (سوريا)",
41129 "Trinidad and Tobago",
41134 "Tunisia (تونس)",
41139 "Turkey (Türkiye)",
41149 "Turks and Caicos Islands",
41159 "U.S. Virgin Islands",
41169 "Ukraine (Україна)",
41174 "United Arab Emirates (الإمارات العربية المتحدة)",
41196 "Uzbekistan (Oʻzbekiston)",
41206 "Vatican City (Città del Vaticano)",
41217 "Vietnam (Việt Nam)",
41222 "Wallis and Futuna (Wallis-et-Futuna)",
41227 "Western Sahara (الصحراء الغربية)",
41233 "Yemen (اليمن)",
41257 * This script refer to:
41258 * Title: International Telephone Input
41259 * Author: Jack O'Connor
41260 * Code version: v12.1.12
41261 * Availability: https://github.com/jackocnr/intl-tel-input.git
41265 * @class Roo.bootstrap.PhoneInput
41266 * @extends Roo.bootstrap.TriggerField
41267 * An input with International dial-code selection
41269 * @cfg {String} defaultDialCode default '+852'
41270 * @cfg {Array} preferedCountries default []
41273 * Create a new PhoneInput.
41274 * @param {Object} config Configuration options
41277 Roo.bootstrap.PhoneInput = function(config) {
41278 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41281 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41283 listWidth: undefined,
41285 selectedClass: 'active',
41287 invalidClass : "has-warning",
41289 validClass: 'has-success',
41291 allowed: '0123456789',
41296 * @cfg {String} defaultDialCode The default dial code when initializing the input
41298 defaultDialCode: '+852',
41301 * @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
41303 preferedCountries: false,
41305 getAutoCreate : function()
41307 var data = Roo.bootstrap.PhoneInputData();
41308 var align = this.labelAlign || this.parentLabelAlign();
41311 this.allCountries = [];
41312 this.dialCodeMapping = [];
41314 for (var i = 0; i < data.length; i++) {
41316 this.allCountries[i] = {
41320 priority: c[3] || 0,
41321 areaCodes: c[4] || null
41323 this.dialCodeMapping[c[2]] = {
41326 priority: c[3] || 0,
41327 areaCodes: c[4] || null
41339 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41340 maxlength: this.max_length,
41341 cls : 'form-control tel-input',
41342 autocomplete: 'new-password'
41345 var hiddenInput = {
41348 cls: 'hidden-tel-input'
41352 hiddenInput.name = this.name;
41355 if (this.disabled) {
41356 input.disabled = true;
41359 var flag_container = {
41376 cls: this.hasFeedback ? 'has-feedback' : '',
41382 cls: 'dial-code-holder',
41389 cls: 'roo-select2-container input-group',
41396 if (this.fieldLabel.length) {
41399 tooltip: 'This field is required'
41405 cls: 'control-label',
41411 html: this.fieldLabel
41414 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41420 if(this.indicatorpos == 'right') {
41421 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41428 if(align == 'left') {
41436 if(this.labelWidth > 12){
41437 label.style = "width: " + this.labelWidth + 'px';
41439 if(this.labelWidth < 13 && this.labelmd == 0){
41440 this.labelmd = this.labelWidth;
41442 if(this.labellg > 0){
41443 label.cls += ' col-lg-' + this.labellg;
41444 input.cls += ' col-lg-' + (12 - this.labellg);
41446 if(this.labelmd > 0){
41447 label.cls += ' col-md-' + this.labelmd;
41448 container.cls += ' col-md-' + (12 - this.labelmd);
41450 if(this.labelsm > 0){
41451 label.cls += ' col-sm-' + this.labelsm;
41452 container.cls += ' col-sm-' + (12 - this.labelsm);
41454 if(this.labelxs > 0){
41455 label.cls += ' col-xs-' + this.labelxs;
41456 container.cls += ' col-xs-' + (12 - this.labelxs);
41466 var settings = this;
41468 ['xs','sm','md','lg'].map(function(size){
41469 if (settings[size]) {
41470 cfg.cls += ' col-' + size + '-' + settings[size];
41474 this.store = new Roo.data.Store({
41475 proxy : new Roo.data.MemoryProxy({}),
41476 reader : new Roo.data.JsonReader({
41487 'name' : 'dialCode',
41491 'name' : 'priority',
41495 'name' : 'areaCodes',
41502 if(!this.preferedCountries) {
41503 this.preferedCountries = [
41510 var p = this.preferedCountries.reverse();
41513 for (var i = 0; i < p.length; i++) {
41514 for (var j = 0; j < this.allCountries.length; j++) {
41515 if(this.allCountries[j].iso2 == p[i]) {
41516 var t = this.allCountries[j];
41517 this.allCountries.splice(j,1);
41518 this.allCountries.unshift(t);
41524 this.store.proxy.data = {
41526 data: this.allCountries
41532 initEvents : function()
41535 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41537 this.indicator = this.indicatorEl();
41538 this.flag = this.flagEl();
41539 this.dialCodeHolder = this.dialCodeHolderEl();
41541 this.trigger = this.el.select('div.flag-box',true).first();
41542 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41547 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41548 _this.list.setWidth(lw);
41551 this.list.on('mouseover', this.onViewOver, this);
41552 this.list.on('mousemove', this.onViewMove, this);
41553 this.inputEl().on("keyup", this.onKeyUp, this);
41554 this.inputEl().on("keypress", this.onKeyPress, this);
41556 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41558 this.view = new Roo.View(this.list, this.tpl, {
41559 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41562 this.view.on('click', this.onViewClick, this);
41563 this.setValue(this.defaultDialCode);
41566 onTriggerClick : function(e)
41568 Roo.log('trigger click');
41573 if(this.isExpanded()){
41575 this.hasFocus = false;
41577 this.store.load({});
41578 this.hasFocus = true;
41583 isExpanded : function()
41585 return this.list.isVisible();
41588 collapse : function()
41590 if(!this.isExpanded()){
41594 Roo.get(document).un('mousedown', this.collapseIf, this);
41595 Roo.get(document).un('mousewheel', this.collapseIf, this);
41596 this.fireEvent('collapse', this);
41600 expand : function()
41604 if(this.isExpanded() || !this.hasFocus){
41608 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41609 this.list.setWidth(lw);
41612 this.restrictHeight();
41614 Roo.get(document).on('mousedown', this.collapseIf, this);
41615 Roo.get(document).on('mousewheel', this.collapseIf, this);
41617 this.fireEvent('expand', this);
41620 restrictHeight : function()
41622 this.list.alignTo(this.inputEl(), this.listAlign);
41623 this.list.alignTo(this.inputEl(), this.listAlign);
41626 onViewOver : function(e, t)
41628 if(this.inKeyMode){
41631 var item = this.view.findItemFromChild(t);
41634 var index = this.view.indexOf(item);
41635 this.select(index, false);
41640 onViewClick : function(view, doFocus, el, e)
41642 var index = this.view.getSelectedIndexes()[0];
41644 var r = this.store.getAt(index);
41647 this.onSelect(r, index);
41649 if(doFocus !== false && !this.blockFocus){
41650 this.inputEl().focus();
41654 onViewMove : function(e, t)
41656 this.inKeyMode = false;
41659 select : function(index, scrollIntoView)
41661 this.selectedIndex = index;
41662 this.view.select(index);
41663 if(scrollIntoView !== false){
41664 var el = this.view.getNode(index);
41666 this.list.scrollChildIntoView(el, false);
41671 createList : function()
41673 this.list = Roo.get(document.body).createChild({
41675 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41676 style: 'display:none'
41679 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41682 collapseIf : function(e)
41684 var in_combo = e.within(this.el);
41685 var in_list = e.within(this.list);
41686 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41688 if (in_combo || in_list || is_list) {
41694 onSelect : function(record, index)
41696 if(this.fireEvent('beforeselect', this, record, index) !== false){
41698 this.setFlagClass(record.data.iso2);
41699 this.setDialCode(record.data.dialCode);
41700 this.hasFocus = false;
41702 this.fireEvent('select', this, record, index);
41706 flagEl : function()
41708 var flag = this.el.select('div.flag',true).first();
41715 dialCodeHolderEl : function()
41717 var d = this.el.select('input.dial-code-holder',true).first();
41724 setDialCode : function(v)
41726 this.dialCodeHolder.dom.value = '+'+v;
41729 setFlagClass : function(n)
41731 this.flag.dom.className = 'flag '+n;
41734 getValue : function()
41736 var v = this.inputEl().getValue();
41737 if(this.dialCodeHolder) {
41738 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41743 setValue : function(v)
41745 var d = this.getDialCode(v);
41747 //invalid dial code
41748 if(v.length == 0 || !d || d.length == 0) {
41750 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41751 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41757 this.setFlagClass(this.dialCodeMapping[d].iso2);
41758 this.setDialCode(d);
41759 this.inputEl().dom.value = v.replace('+'+d,'');
41760 this.hiddenEl().dom.value = this.getValue();
41765 getDialCode : function(v)
41769 if (v.length == 0) {
41770 return this.dialCodeHolder.dom.value;
41774 if (v.charAt(0) != "+") {
41777 var numericChars = "";
41778 for (var i = 1; i < v.length; i++) {
41779 var c = v.charAt(i);
41782 if (this.dialCodeMapping[numericChars]) {
41783 dialCode = v.substr(1, i);
41785 if (numericChars.length == 4) {
41795 this.setValue(this.defaultDialCode);
41799 hiddenEl : function()
41801 return this.el.select('input.hidden-tel-input',true).first();
41804 // after setting val
41805 onKeyUp : function(e){
41806 this.setValue(this.getValue());
41809 onKeyPress : function(e){
41810 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41817 * @class Roo.bootstrap.MoneyField
41818 * @extends Roo.bootstrap.ComboBox
41819 * Bootstrap MoneyField class
41822 * Create a new MoneyField.
41823 * @param {Object} config Configuration options
41826 Roo.bootstrap.MoneyField = function(config) {
41828 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41832 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41835 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41837 allowDecimals : true,
41839 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41841 decimalSeparator : ".",
41843 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41845 decimalPrecision : 0,
41847 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41849 allowNegative : true,
41851 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41855 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41857 minValue : Number.NEGATIVE_INFINITY,
41859 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41861 maxValue : Number.MAX_VALUE,
41863 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41865 minText : "The minimum value for this field is {0}",
41867 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41869 maxText : "The maximum value for this field is {0}",
41871 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41872 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41874 nanText : "{0} is not a valid number",
41876 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41880 * @cfg {String} defaults currency of the MoneyField
41881 * value should be in lkey
41883 defaultCurrency : false,
41885 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41887 thousandsDelimiter : false,
41889 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41900 getAutoCreate : function()
41902 var align = this.labelAlign || this.parentLabelAlign();
41914 cls : 'form-control roo-money-amount-input',
41915 autocomplete: 'new-password'
41918 var hiddenInput = {
41922 cls: 'hidden-number-input'
41925 if(this.max_length) {
41926 input.maxlength = this.max_length;
41930 hiddenInput.name = this.name;
41933 if (this.disabled) {
41934 input.disabled = true;
41937 var clg = 12 - this.inputlg;
41938 var cmd = 12 - this.inputmd;
41939 var csm = 12 - this.inputsm;
41940 var cxs = 12 - this.inputxs;
41944 cls : 'row roo-money-field',
41948 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41952 cls: 'roo-select2-container input-group',
41956 cls : 'form-control roo-money-currency-input',
41957 autocomplete: 'new-password',
41959 name : this.currencyName
41963 cls : 'input-group-addon',
41977 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41981 cls: this.hasFeedback ? 'has-feedback' : '',
41992 if (this.fieldLabel.length) {
41995 tooltip: 'This field is required'
42001 cls: 'control-label',
42007 html: this.fieldLabel
42010 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42016 if(this.indicatorpos == 'right') {
42017 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42024 if(align == 'left') {
42032 if(this.labelWidth > 12){
42033 label.style = "width: " + this.labelWidth + 'px';
42035 if(this.labelWidth < 13 && this.labelmd == 0){
42036 this.labelmd = this.labelWidth;
42038 if(this.labellg > 0){
42039 label.cls += ' col-lg-' + this.labellg;
42040 input.cls += ' col-lg-' + (12 - this.labellg);
42042 if(this.labelmd > 0){
42043 label.cls += ' col-md-' + this.labelmd;
42044 container.cls += ' col-md-' + (12 - this.labelmd);
42046 if(this.labelsm > 0){
42047 label.cls += ' col-sm-' + this.labelsm;
42048 container.cls += ' col-sm-' + (12 - this.labelsm);
42050 if(this.labelxs > 0){
42051 label.cls += ' col-xs-' + this.labelxs;
42052 container.cls += ' col-xs-' + (12 - this.labelxs);
42063 var settings = this;
42065 ['xs','sm','md','lg'].map(function(size){
42066 if (settings[size]) {
42067 cfg.cls += ' col-' + size + '-' + settings[size];
42074 initEvents : function()
42076 this.indicator = this.indicatorEl();
42078 this.initCurrencyEvent();
42080 this.initNumberEvent();
42083 initCurrencyEvent : function()
42086 throw "can not find store for combo";
42089 this.store = Roo.factory(this.store, Roo.data);
42090 this.store.parent = this;
42094 this.triggerEl = this.el.select('.input-group-addon', true).first();
42096 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42101 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42102 _this.list.setWidth(lw);
42105 this.list.on('mouseover', this.onViewOver, this);
42106 this.list.on('mousemove', this.onViewMove, this);
42107 this.list.on('scroll', this.onViewScroll, this);
42110 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42113 this.view = new Roo.View(this.list, this.tpl, {
42114 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42117 this.view.on('click', this.onViewClick, this);
42119 this.store.on('beforeload', this.onBeforeLoad, this);
42120 this.store.on('load', this.onLoad, this);
42121 this.store.on('loadexception', this.onLoadException, this);
42123 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42124 "up" : function(e){
42125 this.inKeyMode = true;
42129 "down" : function(e){
42130 if(!this.isExpanded()){
42131 this.onTriggerClick();
42133 this.inKeyMode = true;
42138 "enter" : function(e){
42141 if(this.fireEvent("specialkey", this, e)){
42142 this.onViewClick(false);
42148 "esc" : function(e){
42152 "tab" : function(e){
42155 if(this.fireEvent("specialkey", this, e)){
42156 this.onViewClick(false);
42164 doRelay : function(foo, bar, hname){
42165 if(hname == 'down' || this.scope.isExpanded()){
42166 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42174 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42178 initNumberEvent : function(e)
42180 this.inputEl().on("keydown" , this.fireKey, this);
42181 this.inputEl().on("focus", this.onFocus, this);
42182 this.inputEl().on("blur", this.onBlur, this);
42184 this.inputEl().relayEvent('keyup', this);
42186 if(this.indicator){
42187 this.indicator.addClass('invisible');
42190 this.originalValue = this.getValue();
42192 if(this.validationEvent == 'keyup'){
42193 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42194 this.inputEl().on('keyup', this.filterValidation, this);
42196 else if(this.validationEvent !== false){
42197 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42200 if(this.selectOnFocus){
42201 this.on("focus", this.preFocus, this);
42204 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42205 this.inputEl().on("keypress", this.filterKeys, this);
42207 this.inputEl().relayEvent('keypress', this);
42210 var allowed = "0123456789";
42212 if(this.allowDecimals){
42213 allowed += this.decimalSeparator;
42216 if(this.allowNegative){
42220 if(this.thousandsDelimiter) {
42224 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42226 var keyPress = function(e){
42228 var k = e.getKey();
42230 var c = e.getCharCode();
42233 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42234 allowed.indexOf(String.fromCharCode(c)) === -1
42240 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42244 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42249 this.inputEl().on("keypress", keyPress, this);
42253 onTriggerClick : function(e)
42260 this.loadNext = false;
42262 if(this.isExpanded()){
42267 this.hasFocus = true;
42269 if(this.triggerAction == 'all') {
42270 this.doQuery(this.allQuery, true);
42274 this.doQuery(this.getRawValue());
42277 getCurrency : function()
42279 var v = this.currencyEl().getValue();
42284 restrictHeight : function()
42286 this.list.alignTo(this.currencyEl(), this.listAlign);
42287 this.list.alignTo(this.currencyEl(), this.listAlign);
42290 onViewClick : function(view, doFocus, el, e)
42292 var index = this.view.getSelectedIndexes()[0];
42294 var r = this.store.getAt(index);
42297 this.onSelect(r, index);
42301 onSelect : function(record, index){
42303 if(this.fireEvent('beforeselect', this, record, index) !== false){
42305 this.setFromCurrencyData(index > -1 ? record.data : false);
42309 this.fireEvent('select', this, record, index);
42313 setFromCurrencyData : function(o)
42317 this.lastCurrency = o;
42319 if (this.currencyField) {
42320 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42322 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42325 this.lastSelectionText = currency;
42327 //setting default currency
42328 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42329 this.setCurrency(this.defaultCurrency);
42333 this.setCurrency(currency);
42336 setFromData : function(o)
42340 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42342 this.setFromCurrencyData(c);
42347 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42349 Roo.log('no value set for '+ (this.name ? this.name : this.id));
42352 this.setValue(value);
42356 setCurrency : function(v)
42358 this.currencyValue = v;
42361 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42366 setValue : function(v)
42368 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42374 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42376 this.inputEl().dom.value = (v == '') ? '' :
42377 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42379 if(!this.allowZero && v === '0') {
42380 this.hiddenEl().dom.value = '';
42381 this.inputEl().dom.value = '';
42388 getRawValue : function()
42390 var v = this.inputEl().getValue();
42395 getValue : function()
42397 return this.fixPrecision(this.parseValue(this.getRawValue()));
42400 parseValue : function(value)
42402 if(this.thousandsDelimiter) {
42404 r = new RegExp(",", "g");
42405 value = value.replace(r, "");
42408 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42409 return isNaN(value) ? '' : value;
42413 fixPrecision : function(value)
42415 if(this.thousandsDelimiter) {
42417 r = new RegExp(",", "g");
42418 value = value.replace(r, "");
42421 var nan = isNaN(value);
42423 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42424 return nan ? '' : value;
42426 return parseFloat(value).toFixed(this.decimalPrecision);
42429 decimalPrecisionFcn : function(v)
42431 return Math.floor(v);
42434 validateValue : function(value)
42436 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42440 var num = this.parseValue(value);
42443 this.markInvalid(String.format(this.nanText, value));
42447 if(num < this.minValue){
42448 this.markInvalid(String.format(this.minText, this.minValue));
42452 if(num > this.maxValue){
42453 this.markInvalid(String.format(this.maxText, this.maxValue));
42460 validate : function()
42462 if(this.disabled || this.allowBlank){
42467 var currency = this.getCurrency();
42469 if(this.validateValue(this.getRawValue()) && currency.length){
42474 this.markInvalid();
42478 getName: function()
42483 beforeBlur : function()
42489 var v = this.parseValue(this.getRawValue());
42496 onBlur : function()
42500 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42501 //this.el.removeClass(this.focusClass);
42504 this.hasFocus = false;
42506 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42510 var v = this.getValue();
42512 if(String(v) !== String(this.startValue)){
42513 this.fireEvent('change', this, v, this.startValue);
42516 this.fireEvent("blur", this);
42519 inputEl : function()
42521 return this.el.select('.roo-money-amount-input', true).first();
42524 currencyEl : function()
42526 return this.el.select('.roo-money-currency-input', true).first();
42529 hiddenEl : function()
42531 return this.el.select('input.hidden-number-input',true).first();
42535 * @class Roo.bootstrap.BezierSignature
42536 * @extends Roo.bootstrap.Component
42537 * Bootstrap BezierSignature class
42538 * This script refer to:
42539 * Title: Signature Pad
42541 * Availability: https://github.com/szimek/signature_pad
42544 * Create a new BezierSignature
42545 * @param {Object} config The config object
42548 Roo.bootstrap.BezierSignature = function(config){
42549 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42555 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42562 mouse_btn_down: true,
42565 * @cfg {int} canvas height
42567 canvas_height: '200px',
42570 * @cfg {float|function} Radius of a single dot.
42575 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42580 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42585 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42590 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42595 * @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.
42597 bg_color: 'rgba(0, 0, 0, 0)',
42600 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42602 dot_color: 'black',
42605 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42607 velocity_filter_weight: 0.7,
42610 * @cfg {function} Callback when stroke begin.
42615 * @cfg {function} Callback when stroke end.
42619 getAutoCreate : function()
42621 var cls = 'roo-signature column';
42624 cls += ' ' + this.cls;
42634 for(var i = 0; i < col_sizes.length; i++) {
42635 if(this[col_sizes[i]]) {
42636 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42646 cls: 'roo-signature-body',
42650 cls: 'roo-signature-body-canvas',
42651 height: this.canvas_height,
42652 width: this.canvas_width
42659 style: 'display: none'
42667 initEvents: function()
42669 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42671 var canvas = this.canvasEl();
42673 // mouse && touch event swapping...
42674 canvas.dom.style.touchAction = 'none';
42675 canvas.dom.style.msTouchAction = 'none';
42677 this.mouse_btn_down = false;
42678 canvas.on('mousedown', this._handleMouseDown, this);
42679 canvas.on('mousemove', this._handleMouseMove, this);
42680 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42682 if (window.PointerEvent) {
42683 canvas.on('pointerdown', this._handleMouseDown, this);
42684 canvas.on('pointermove', this._handleMouseMove, this);
42685 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42688 if ('ontouchstart' in window) {
42689 canvas.on('touchstart', this._handleTouchStart, this);
42690 canvas.on('touchmove', this._handleTouchMove, this);
42691 canvas.on('touchend', this._handleTouchEnd, this);
42694 Roo.EventManager.onWindowResize(this.resize, this, true);
42696 // file input event
42697 this.fileEl().on('change', this.uploadImage, this);
42704 resize: function(){
42706 var canvas = this.canvasEl().dom;
42707 var ctx = this.canvasElCtx();
42708 var img_data = false;
42710 if(canvas.width > 0) {
42711 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42713 // setting canvas width will clean img data
42716 var style = window.getComputedStyle ?
42717 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42719 var padding_left = parseInt(style.paddingLeft) || 0;
42720 var padding_right = parseInt(style.paddingRight) || 0;
42722 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42725 ctx.putImageData(img_data, 0, 0);
42729 _handleMouseDown: function(e)
42731 if (e.browserEvent.which === 1) {
42732 this.mouse_btn_down = true;
42733 this.strokeBegin(e);
42737 _handleMouseMove: function (e)
42739 if (this.mouse_btn_down) {
42740 this.strokeMoveUpdate(e);
42744 _handleMouseUp: function (e)
42746 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42747 this.mouse_btn_down = false;
42752 _handleTouchStart: function (e) {
42754 e.preventDefault();
42755 if (e.browserEvent.targetTouches.length === 1) {
42756 // var touch = e.browserEvent.changedTouches[0];
42757 // this.strokeBegin(touch);
42759 this.strokeBegin(e); // assume e catching the correct xy...
42763 _handleTouchMove: function (e) {
42764 e.preventDefault();
42765 // var touch = event.targetTouches[0];
42766 // _this._strokeMoveUpdate(touch);
42767 this.strokeMoveUpdate(e);
42770 _handleTouchEnd: function (e) {
42771 var wasCanvasTouched = e.target === this.canvasEl().dom;
42772 if (wasCanvasTouched) {
42773 e.preventDefault();
42774 // var touch = event.changedTouches[0];
42775 // _this._strokeEnd(touch);
42780 reset: function () {
42781 this._lastPoints = [];
42782 this._lastVelocity = 0;
42783 this._lastWidth = (this.min_width + this.max_width) / 2;
42784 this.canvasElCtx().fillStyle = this.dot_color;
42787 strokeMoveUpdate: function(e)
42789 this.strokeUpdate(e);
42791 if (this.throttle) {
42792 this.throttleStroke(this.strokeUpdate, this.throttle);
42795 this.strokeUpdate(e);
42799 strokeBegin: function(e)
42801 var newPointGroup = {
42802 color: this.dot_color,
42806 if (typeof this.onBegin === 'function') {
42810 this.curve_data.push(newPointGroup);
42812 this.strokeUpdate(e);
42815 strokeUpdate: function(e)
42817 var rect = this.canvasEl().dom.getBoundingClientRect();
42818 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42819 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42820 var lastPoints = lastPointGroup.points;
42821 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42822 var isLastPointTooClose = lastPoint
42823 ? point.distanceTo(lastPoint) <= this.min_distance
42825 var color = lastPointGroup.color;
42826 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42827 var curve = this.addPoint(point);
42829 this.drawDot({color: color, point: point});
42832 this.drawCurve({color: color, curve: curve});
42842 strokeEnd: function(e)
42844 this.strokeUpdate(e);
42845 if (typeof this.onEnd === 'function') {
42850 addPoint: function (point) {
42851 var _lastPoints = this._lastPoints;
42852 _lastPoints.push(point);
42853 if (_lastPoints.length > 2) {
42854 if (_lastPoints.length === 3) {
42855 _lastPoints.unshift(_lastPoints[0]);
42857 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42858 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42859 _lastPoints.shift();
42865 calculateCurveWidths: function (startPoint, endPoint) {
42866 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42867 (1 - this.velocity_filter_weight) * this._lastVelocity;
42869 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42872 start: this._lastWidth
42875 this._lastVelocity = velocity;
42876 this._lastWidth = newWidth;
42880 drawDot: function (_a) {
42881 var color = _a.color, point = _a.point;
42882 var ctx = this.canvasElCtx();
42883 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42885 this.drawCurveSegment(point.x, point.y, width);
42887 ctx.fillStyle = color;
42891 drawCurve: function (_a) {
42892 var color = _a.color, curve = _a.curve;
42893 var ctx = this.canvasElCtx();
42894 var widthDelta = curve.endWidth - curve.startWidth;
42895 var drawSteps = Math.floor(curve.length()) * 2;
42897 ctx.fillStyle = color;
42898 for (var i = 0; i < drawSteps; i += 1) {
42899 var t = i / drawSteps;
42905 var x = uuu * curve.startPoint.x;
42906 x += 3 * uu * t * curve.control1.x;
42907 x += 3 * u * tt * curve.control2.x;
42908 x += ttt * curve.endPoint.x;
42909 var y = uuu * curve.startPoint.y;
42910 y += 3 * uu * t * curve.control1.y;
42911 y += 3 * u * tt * curve.control2.y;
42912 y += ttt * curve.endPoint.y;
42913 var width = curve.startWidth + ttt * widthDelta;
42914 this.drawCurveSegment(x, y, width);
42920 drawCurveSegment: function (x, y, width) {
42921 var ctx = this.canvasElCtx();
42923 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42924 this.is_empty = false;
42929 var ctx = this.canvasElCtx();
42930 var canvas = this.canvasEl().dom;
42931 ctx.fillStyle = this.bg_color;
42932 ctx.clearRect(0, 0, canvas.width, canvas.height);
42933 ctx.fillRect(0, 0, canvas.width, canvas.height);
42934 this.curve_data = [];
42936 this.is_empty = true;
42941 return this.el.select('input',true).first();
42944 canvasEl: function()
42946 return this.el.select('canvas',true).first();
42949 canvasElCtx: function()
42951 return this.el.select('canvas',true).first().dom.getContext('2d');
42954 getImage: function(type)
42956 if(this.is_empty) {
42961 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42964 drawFromImage: function(img_src)
42966 var img = new Image();
42968 img.onload = function(){
42969 this.canvasElCtx().drawImage(img, 0, 0);
42974 this.is_empty = false;
42977 selectImage: function()
42979 this.fileEl().dom.click();
42982 uploadImage: function(e)
42984 var reader = new FileReader();
42986 reader.onload = function(e){
42987 var img = new Image();
42988 img.onload = function(){
42990 this.canvasElCtx().drawImage(img, 0, 0);
42992 img.src = e.target.result;
42995 reader.readAsDataURL(e.target.files[0]);
42998 // Bezier Point Constructor
42999 Point: (function () {
43000 function Point(x, y, time) {
43003 this.time = time || Date.now();
43005 Point.prototype.distanceTo = function (start) {
43006 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43008 Point.prototype.equals = function (other) {
43009 return this.x === other.x && this.y === other.y && this.time === other.time;
43011 Point.prototype.velocityFrom = function (start) {
43012 return this.time !== start.time
43013 ? this.distanceTo(start) / (this.time - start.time)
43020 // Bezier Constructor
43021 Bezier: (function () {
43022 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43023 this.startPoint = startPoint;
43024 this.control2 = control2;
43025 this.control1 = control1;
43026 this.endPoint = endPoint;
43027 this.startWidth = startWidth;
43028 this.endWidth = endWidth;
43030 Bezier.fromPoints = function (points, widths, scope) {
43031 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43032 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43033 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43035 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43036 var dx1 = s1.x - s2.x;
43037 var dy1 = s1.y - s2.y;
43038 var dx2 = s2.x - s3.x;
43039 var dy2 = s2.y - s3.y;
43040 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43041 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43042 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43043 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43044 var dxm = m1.x - m2.x;
43045 var dym = m1.y - m2.y;
43046 var k = l2 / (l1 + l2);
43047 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43048 var tx = s2.x - cm.x;
43049 var ty = s2.y - cm.y;
43051 c1: new scope.Point(m1.x + tx, m1.y + ty),
43052 c2: new scope.Point(m2.x + tx, m2.y + ty)
43055 Bezier.prototype.length = function () {
43060 for (var i = 0; i <= steps; i += 1) {
43062 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43063 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43065 var xdiff = cx - px;
43066 var ydiff = cy - py;
43067 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43074 Bezier.prototype.point = function (t, start, c1, c2, end) {
43075 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43076 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43077 + (3.0 * c2 * (1.0 - t) * t * t)
43078 + (end * t * t * t);
43083 throttleStroke: function(fn, wait) {
43084 if (wait === void 0) { wait = 250; }
43086 var timeout = null;
43090 var later = function () {
43091 previous = Date.now();
43093 result = fn.apply(storedContext, storedArgs);
43095 storedContext = null;
43099 return function wrapper() {
43101 for (var _i = 0; _i < arguments.length; _i++) {
43102 args[_i] = arguments[_i];
43104 var now = Date.now();
43105 var remaining = wait - (now - previous);
43106 storedContext = this;
43108 if (remaining <= 0 || remaining > wait) {
43110 clearTimeout(timeout);
43114 result = fn.apply(storedContext, storedArgs);
43116 storedContext = null;
43120 else if (!timeout) {
43121 timeout = window.setTimeout(later, remaining);