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 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2258 // see if target is one of the 'cards'...
2259 var ctarget = false;
2261 //Roo.log(this.items.length);
2262 var lpos = pos = false;
2263 for (var i = 0;i< this.items.length;i++) {
2265 if (!this.items[i].el.hasClass('card')) {
2268 pos = this.getDropPoint(e, this.items[i].el.dom);
2270 //Roo.log(this.items[i].el.dom.id);
2271 cards.push(this.items[i]);
2272 if (pos == 'above') {
2273 ctarget = this.items[i > 0 ? i-1 : 0];
2274 pos = i > 0 ? 'below' : pos;
2280 ctarget = cards[cards.length-1] || this.el.dom;
2285 //Roo.log(['getTargetFromEvent', ctarget]);
2286 return [ ctarget, pos ];
2289 onNodeEnter : function(n, dd, e, data){
2292 onNodeOver : function(n, dd, e, data)
2294 // Roo.log(['onNodeOver'])
2296 var pt = this.getDropPoint(e, n, dd);
2297 // set the insert point style on the target node
2298 //var dragElClass = this.dropNotAllowed;
2300 this.dropPlaceHolder('hide');
2305 target_info = this.getTargetFromEvent(e);
2306 Roo.log(['getTargetFromEvent', target_info[0].el.dom.id,target_info[1]]);
2309 this.dropPlaceHolder('show', targetinfo,data);
2310 return false; //dragElClass;
2312 onNodeOut : function(n, dd, e, data){
2313 //this.removeDropIndicators(n);
2315 onNodeDrop : function(n, dd, e, data)
2319 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2322 var pt = this.getDropPoint(e, n, dd);
2323 var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2324 if (pt == "below") {
2327 for (var i = 0; i < this.items.length; i++) {
2328 var r = this.items[i];
2329 //var dup = this.store.getById(r.id);
2330 if (dup && (dd != this.dragZone)) {
2331 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2334 this.store.insert(insertAt++, r.copy());
2336 data.source.isDirtyFlag = true;
2338 this.store.insert(insertAt++, r);
2340 this.isDirtyFlag = true;
2343 this.dragZone.cachedTarget = null;
2347 /** Decide whether to drop above or below a View node. */
2348 getDropPoint : function(e, n, dd)
2353 if (n == this.bodyEl.dom) {
2356 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2357 var c = t + (b - t) / 2;
2358 var y = Roo.lib.Event.getPageY(e);
2365 onToggleCollapse : function(e)
2367 if (this.collapsed) {
2368 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2369 this.el.select('.roo-collapsable').addClass('show');
2370 this.collapsed = false;
2373 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2374 this.el.select('.roo-collapsable').removeClass('show');
2375 this.collapsed = true;
2379 dropPlaceHolder: function (action, where_ar, data)
2381 if (this.dropEl === false) {
2382 this.dropEl = new Roo.DomHelper.append(this.dom.bodyEl, {
2387 if (action == 'hide') {
2388 this.dropEl.removeClass(['d-none', 'd-block']);
2389 this.dropEl.addClass('d-none');
2392 var cardel = where_ar[0].el.dom;
2407 * Card header - holder for the card header elements.
2412 * @class Roo.bootstrap.CardHeader
2413 * @extends Roo.bootstrap.Element
2414 * Bootstrap CardHeader class
2416 * Create a new Card Header - that you can embed children into
2417 * @param {Object} config The config object
2420 Roo.bootstrap.CardHeader = function(config){
2421 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2424 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2427 container_method : 'getCardHeader'
2440 * Card header - holder for the card header elements.
2445 * @class Roo.bootstrap.CardImageTop
2446 * @extends Roo.bootstrap.Element
2447 * Bootstrap CardImageTop class
2449 * Create a new Card Image Top container
2450 * @param {Object} config The config object
2453 Roo.bootstrap.CardImageTop = function(config){
2454 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2457 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2460 container_method : 'getCardImageTop'
2478 * @class Roo.bootstrap.Img
2479 * @extends Roo.bootstrap.Component
2480 * Bootstrap Img class
2481 * @cfg {Boolean} imgResponsive false | true
2482 * @cfg {String} border rounded | circle | thumbnail
2483 * @cfg {String} src image source
2484 * @cfg {String} alt image alternative text
2485 * @cfg {String} href a tag href
2486 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2487 * @cfg {String} xsUrl xs image source
2488 * @cfg {String} smUrl sm image source
2489 * @cfg {String} mdUrl md image source
2490 * @cfg {String} lgUrl lg image source
2493 * Create a new Input
2494 * @param {Object} config The config object
2497 Roo.bootstrap.Img = function(config){
2498 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2504 * The img click event for the img.
2505 * @param {Roo.EventObject} e
2511 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2513 imgResponsive: true,
2523 getAutoCreate : function()
2525 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2526 return this.createSingleImg();
2531 cls: 'roo-image-responsive-group',
2536 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2538 if(!_this[size + 'Url']){
2544 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2545 html: _this.html || cfg.html,
2546 src: _this[size + 'Url']
2549 img.cls += ' roo-image-responsive-' + size;
2551 var s = ['xs', 'sm', 'md', 'lg'];
2553 s.splice(s.indexOf(size), 1);
2555 Roo.each(s, function(ss){
2556 img.cls += ' hidden-' + ss;
2559 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2560 cfg.cls += ' img-' + _this.border;
2564 cfg.alt = _this.alt;
2577 a.target = _this.target;
2581 cfg.cn.push((_this.href) ? a : img);
2588 createSingleImg : function()
2592 cls: (this.imgResponsive) ? 'img-responsive' : '',
2594 src : 'about:blank' // just incase src get's set to undefined?!?
2597 cfg.html = this.html || cfg.html;
2599 cfg.src = this.src || cfg.src;
2601 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2602 cfg.cls += ' img-' + this.border;
2619 a.target = this.target;
2624 return (this.href) ? a : cfg;
2627 initEvents: function()
2630 this.el.on('click', this.onClick, this);
2635 onClick : function(e)
2637 Roo.log('img onclick');
2638 this.fireEvent('click', this, e);
2641 * Sets the url of the image - used to update it
2642 * @param {String} url the url of the image
2645 setSrc : function(url)
2649 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2650 this.el.dom.src = url;
2654 this.el.select('img', true).first().dom.src = url;
2670 * @class Roo.bootstrap.Link
2671 * @extends Roo.bootstrap.Component
2672 * Bootstrap Link Class
2673 * @cfg {String} alt image alternative text
2674 * @cfg {String} href a tag href
2675 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2676 * @cfg {String} html the content of the link.
2677 * @cfg {String} anchor name for the anchor link
2678 * @cfg {String} fa - favicon
2680 * @cfg {Boolean} preventDefault (true | false) default false
2684 * Create a new Input
2685 * @param {Object} config The config object
2688 Roo.bootstrap.Link = function(config){
2689 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2695 * The img click event for the img.
2696 * @param {Roo.EventObject} e
2702 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2706 preventDefault: false,
2712 getAutoCreate : function()
2714 var html = this.html || '';
2716 if (this.fa !== false) {
2717 html = '<i class="fa fa-' + this.fa + '"></i>';
2722 // anchor's do not require html/href...
2723 if (this.anchor === false) {
2725 cfg.href = this.href || '#';
2727 cfg.name = this.anchor;
2728 if (this.html !== false || this.fa !== false) {
2731 if (this.href !== false) {
2732 cfg.href = this.href;
2736 if(this.alt !== false){
2741 if(this.target !== false) {
2742 cfg.target = this.target;
2748 initEvents: function() {
2750 if(!this.href || this.preventDefault){
2751 this.el.on('click', this.onClick, this);
2755 onClick : function(e)
2757 if(this.preventDefault){
2760 //Roo.log('img onclick');
2761 this.fireEvent('click', this, e);
2774 * @class Roo.bootstrap.Header
2775 * @extends Roo.bootstrap.Component
2776 * Bootstrap Header class
2777 * @cfg {String} html content of header
2778 * @cfg {Number} level (1|2|3|4|5|6) default 1
2781 * Create a new Header
2782 * @param {Object} config The config object
2786 Roo.bootstrap.Header = function(config){
2787 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2790 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2798 getAutoCreate : function(){
2803 tag: 'h' + (1 *this.level),
2804 html: this.html || ''
2816 * Ext JS Library 1.1.1
2817 * Copyright(c) 2006-2007, Ext JS, LLC.
2819 * Originally Released Under LGPL - original licence link has changed is not relivant.
2822 * <script type="text/javascript">
2826 * @class Roo.bootstrap.MenuMgr
2827 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2830 Roo.bootstrap.MenuMgr = function(){
2831 var menus, active, groups = {}, attached = false, lastShow = new Date();
2833 // private - called when first menu is created
2836 active = new Roo.util.MixedCollection();
2837 Roo.get(document).addKeyListener(27, function(){
2838 if(active.length > 0){
2846 if(active && active.length > 0){
2847 var c = active.clone();
2857 if(active.length < 1){
2858 Roo.get(document).un("mouseup", onMouseDown);
2866 var last = active.last();
2867 lastShow = new Date();
2870 Roo.get(document).on("mouseup", onMouseDown);
2875 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2876 m.parentMenu.activeChild = m;
2877 }else if(last && last.isVisible()){
2878 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2883 function onBeforeHide(m){
2885 m.activeChild.hide();
2887 if(m.autoHideTimer){
2888 clearTimeout(m.autoHideTimer);
2889 delete m.autoHideTimer;
2894 function onBeforeShow(m){
2895 var pm = m.parentMenu;
2896 if(!pm && !m.allowOtherMenus){
2898 }else if(pm && pm.activeChild && active != m){
2899 pm.activeChild.hide();
2903 // private this should really trigger on mouseup..
2904 function onMouseDown(e){
2905 Roo.log("on Mouse Up");
2907 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2908 Roo.log("MenuManager hideAll");
2917 function onBeforeCheck(mi, state){
2919 var g = groups[mi.group];
2920 for(var i = 0, l = g.length; i < l; i++){
2922 g[i].setChecked(false);
2931 * Hides all menus that are currently visible
2933 hideAll : function(){
2938 register : function(menu){
2942 menus[menu.id] = menu;
2943 menu.on("beforehide", onBeforeHide);
2944 menu.on("hide", onHide);
2945 menu.on("beforeshow", onBeforeShow);
2946 menu.on("show", onShow);
2948 if(g && menu.events["checkchange"]){
2952 groups[g].push(menu);
2953 menu.on("checkchange", onCheck);
2958 * Returns a {@link Roo.menu.Menu} object
2959 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2960 * be used to generate and return a new Menu instance.
2962 get : function(menu){
2963 if(typeof menu == "string"){ // menu id
2965 }else if(menu.events){ // menu instance
2968 /*else if(typeof menu.length == 'number'){ // array of menu items?
2969 return new Roo.bootstrap.Menu({items:menu});
2970 }else{ // otherwise, must be a config
2971 return new Roo.bootstrap.Menu(menu);
2978 unregister : function(menu){
2979 delete menus[menu.id];
2980 menu.un("beforehide", onBeforeHide);
2981 menu.un("hide", onHide);
2982 menu.un("beforeshow", onBeforeShow);
2983 menu.un("show", onShow);
2985 if(g && menu.events["checkchange"]){
2986 groups[g].remove(menu);
2987 menu.un("checkchange", onCheck);
2992 registerCheckable : function(menuItem){
2993 var g = menuItem.group;
2998 groups[g].push(menuItem);
2999 menuItem.on("beforecheckchange", onBeforeCheck);
3004 unregisterCheckable : function(menuItem){
3005 var g = menuItem.group;
3007 groups[g].remove(menuItem);
3008 menuItem.un("beforecheckchange", onBeforeCheck);
3020 * @class Roo.bootstrap.Menu
3021 * @extends Roo.bootstrap.Component
3022 * Bootstrap Menu class - container for MenuItems
3023 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3024 * @cfg {bool} hidden if the menu should be hidden when rendered.
3025 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3026 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3030 * @param {Object} config The config object
3034 Roo.bootstrap.Menu = function(config){
3035 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3036 if (this.registerMenu && this.type != 'treeview') {
3037 Roo.bootstrap.MenuMgr.register(this);
3044 * Fires before this menu is displayed (return false to block)
3045 * @param {Roo.menu.Menu} this
3050 * Fires before this menu is hidden (return false to block)
3051 * @param {Roo.menu.Menu} this
3056 * Fires after this menu is displayed
3057 * @param {Roo.menu.Menu} this
3062 * Fires after this menu is hidden
3063 * @param {Roo.menu.Menu} this
3068 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3069 * @param {Roo.menu.Menu} this
3070 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3071 * @param {Roo.EventObject} e
3076 * Fires when the mouse is hovering over this menu
3077 * @param {Roo.menu.Menu} this
3078 * @param {Roo.EventObject} e
3079 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3084 * Fires when the mouse exits this menu
3085 * @param {Roo.menu.Menu} this
3086 * @param {Roo.EventObject} e
3087 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3092 * Fires when a menu item contained in this menu is clicked
3093 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3094 * @param {Roo.EventObject} e
3098 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3101 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3105 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3108 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3110 registerMenu : true,
3112 menuItems :false, // stores the menu items..
3122 getChildContainer : function() {
3126 getAutoCreate : function(){
3128 //if (['right'].indexOf(this.align)!==-1) {
3129 // cfg.cn[1].cls += ' pull-right'
3135 cls : 'dropdown-menu' ,
3136 style : 'z-index:1000'
3140 if (this.type === 'submenu') {
3141 cfg.cls = 'submenu active';
3143 if (this.type === 'treeview') {
3144 cfg.cls = 'treeview-menu';
3149 initEvents : function() {
3151 // Roo.log("ADD event");
3152 // Roo.log(this.triggerEl.dom);
3154 this.triggerEl.on('click', this.onTriggerClick, this);
3156 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3159 if (this.triggerEl.hasClass('nav-item')) {
3160 // dropdown toggle on the 'a' in BS4?
3161 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3163 this.triggerEl.addClass('dropdown-toggle');
3166 this.el.on('touchstart' , this.onTouch, this);
3168 this.el.on('click' , this.onClick, this);
3170 this.el.on("mouseover", this.onMouseOver, this);
3171 this.el.on("mouseout", this.onMouseOut, this);
3175 findTargetItem : function(e)
3177 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3181 //Roo.log(t); Roo.log(t.id);
3183 //Roo.log(this.menuitems);
3184 return this.menuitems.get(t.id);
3186 //return this.items.get(t.menuItemId);
3192 onTouch : function(e)
3194 Roo.log("menu.onTouch");
3195 //e.stopEvent(); this make the user popdown broken
3199 onClick : function(e)
3201 Roo.log("menu.onClick");
3203 var t = this.findTargetItem(e);
3204 if(!t || t.isContainer){
3209 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3210 if(t == this.activeItem && t.shouldDeactivate(e)){
3211 this.activeItem.deactivate();
3212 delete this.activeItem;
3216 this.setActiveItem(t, true);
3224 Roo.log('pass click event');
3228 this.fireEvent("click", this, t, e);
3232 if(!t.href.length || t.href == '#'){
3233 (function() { _this.hide(); }).defer(100);
3238 onMouseOver : function(e){
3239 var t = this.findTargetItem(e);
3242 // if(t.canActivate && !t.disabled){
3243 // this.setActiveItem(t, true);
3247 this.fireEvent("mouseover", this, e, t);
3249 isVisible : function(){
3250 return !this.hidden;
3252 onMouseOut : function(e){
3253 var t = this.findTargetItem(e);
3256 // if(t == this.activeItem && t.shouldDeactivate(e)){
3257 // this.activeItem.deactivate();
3258 // delete this.activeItem;
3261 this.fireEvent("mouseout", this, e, t);
3266 * Displays this menu relative to another element
3267 * @param {String/HTMLElement/Roo.Element} element The element to align to
3268 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3269 * the element (defaults to this.defaultAlign)
3270 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3272 show : function(el, pos, parentMenu)
3274 if (false === this.fireEvent("beforeshow", this)) {
3275 Roo.log("show canceled");
3278 this.parentMenu = parentMenu;
3283 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3286 * Displays this menu at a specific xy position
3287 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3288 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3290 showAt : function(xy, parentMenu, /* private: */_e){
3291 this.parentMenu = parentMenu;
3296 this.fireEvent("beforeshow", this);
3297 //xy = this.el.adjustForConstraints(xy);
3301 this.hideMenuItems();
3302 this.hidden = false;
3303 this.triggerEl.addClass('open');
3304 this.el.addClass('show');
3306 // reassign x when hitting right
3307 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3308 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3311 // reassign y when hitting bottom
3312 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3313 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3316 // but the list may align on trigger left or trigger top... should it be a properity?
3318 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3323 this.fireEvent("show", this);
3329 this.doFocus.defer(50, this);
3333 doFocus : function(){
3335 this.focusEl.focus();
3340 * Hides this menu and optionally all parent menus
3341 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3343 hide : function(deep)
3345 if (false === this.fireEvent("beforehide", this)) {
3346 Roo.log("hide canceled");
3349 this.hideMenuItems();
3350 if(this.el && this.isVisible()){
3352 if(this.activeItem){
3353 this.activeItem.deactivate();
3354 this.activeItem = null;
3356 this.triggerEl.removeClass('open');;
3357 this.el.removeClass('show');
3359 this.fireEvent("hide", this);
3361 if(deep === true && this.parentMenu){
3362 this.parentMenu.hide(true);
3366 onTriggerClick : function(e)
3368 Roo.log('trigger click');
3370 var target = e.getTarget();
3372 Roo.log(target.nodeName.toLowerCase());
3374 if(target.nodeName.toLowerCase() === 'i'){
3380 onTriggerPress : function(e)
3382 Roo.log('trigger press');
3383 //Roo.log(e.getTarget());
3384 // Roo.log(this.triggerEl.dom);
3386 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3387 var pel = Roo.get(e.getTarget());
3388 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3389 Roo.log('is treeview or dropdown?');
3393 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3397 if (this.isVisible()) {
3402 this.show(this.triggerEl, '?', false);
3405 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3412 hideMenuItems : function()
3414 Roo.log("hide Menu Items");
3419 this.el.select('.open',true).each(function(aa) {
3421 aa.removeClass('open');
3425 addxtypeChild : function (tree, cntr) {
3426 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3428 this.menuitems.add(comp);
3440 this.getEl().dom.innerHTML = '';
3441 this.menuitems.clear();
3455 * @class Roo.bootstrap.MenuItem
3456 * @extends Roo.bootstrap.Component
3457 * Bootstrap MenuItem class
3458 * @cfg {String} html the menu label
3459 * @cfg {String} href the link
3460 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3461 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3462 * @cfg {Boolean} active used on sidebars to highlight active itesm
3463 * @cfg {String} fa favicon to show on left of menu item.
3464 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3468 * Create a new MenuItem
3469 * @param {Object} config The config object
3473 Roo.bootstrap.MenuItem = function(config){
3474 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3479 * The raw click event for the entire grid.
3480 * @param {Roo.bootstrap.MenuItem} this
3481 * @param {Roo.EventObject} e
3487 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3491 preventDefault: false,
3492 isContainer : false,
3496 getAutoCreate : function(){
3498 if(this.isContainer){
3501 cls: 'dropdown-menu-item '
3511 cls : 'dropdown-item',
3516 if (this.fa !== false) {
3519 cls : 'fa fa-' + this.fa
3528 cls: 'dropdown-menu-item',
3531 if (this.parent().type == 'treeview') {
3532 cfg.cls = 'treeview-menu';
3535 cfg.cls += ' active';
3540 anc.href = this.href || cfg.cn[0].href ;
3541 ctag.html = this.html || cfg.cn[0].html ;
3545 initEvents: function()
3547 if (this.parent().type == 'treeview') {
3548 this.el.select('a').on('click', this.onClick, this);
3552 this.menu.parentType = this.xtype;
3553 this.menu.triggerEl = this.el;
3554 this.menu = this.addxtype(Roo.apply({}, this.menu));
3558 onClick : function(e)
3560 Roo.log('item on click ');
3562 if(this.preventDefault){
3565 //this.parent().hideMenuItems();
3567 this.fireEvent('click', this, e);
3586 * @class Roo.bootstrap.MenuSeparator
3587 * @extends Roo.bootstrap.Component
3588 * Bootstrap MenuSeparator class
3591 * Create a new MenuItem
3592 * @param {Object} config The config object
3596 Roo.bootstrap.MenuSeparator = function(config){
3597 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3600 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3602 getAutoCreate : function(){
3621 * @class Roo.bootstrap.Modal
3622 * @extends Roo.bootstrap.Component
3623 * Bootstrap Modal class
3624 * @cfg {String} title Title of dialog
3625 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3626 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3627 * @cfg {Boolean} specificTitle default false
3628 * @cfg {Array} buttons Array of buttons or standard button set..
3629 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3630 * @cfg {Boolean} animate default true
3631 * @cfg {Boolean} allow_close default true
3632 * @cfg {Boolean} fitwindow default false
3633 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3634 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3635 * @cfg {String} size (sm|lg) default empty
3636 * @cfg {Number} max_width set the max width of modal
3640 * Create a new Modal Dialog
3641 * @param {Object} config The config object
3644 Roo.bootstrap.Modal = function(config){
3645 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3650 * The raw btnclick event for the button
3651 * @param {Roo.EventObject} e
3656 * Fire when dialog resize
3657 * @param {Roo.bootstrap.Modal} this
3658 * @param {Roo.EventObject} e
3662 this.buttons = this.buttons || [];
3665 this.tmpl = Roo.factory(this.tmpl);
3670 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3672 title : 'test dialog',
3682 specificTitle: false,
3684 buttonPosition: 'right',
3707 onRender : function(ct, position)
3709 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3712 var cfg = Roo.apply({}, this.getAutoCreate());
3715 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3717 //if (!cfg.name.length) {
3721 cfg.cls += ' ' + this.cls;
3724 cfg.style = this.style;
3726 this.el = Roo.get(document.body).createChild(cfg, position);
3728 //var type = this.el.dom.type;
3731 if(this.tabIndex !== undefined){
3732 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3735 this.dialogEl = this.el.select('.modal-dialog',true).first();
3736 this.bodyEl = this.el.select('.modal-body',true).first();
3737 this.closeEl = this.el.select('.modal-header .close', true).first();
3738 this.headerEl = this.el.select('.modal-header',true).first();
3739 this.titleEl = this.el.select('.modal-title',true).first();
3740 this.footerEl = this.el.select('.modal-footer',true).first();
3742 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3744 //this.el.addClass("x-dlg-modal");
3746 if (this.buttons.length) {
3747 Roo.each(this.buttons, function(bb) {
3748 var b = Roo.apply({}, bb);
3749 b.xns = b.xns || Roo.bootstrap;
3750 b.xtype = b.xtype || 'Button';
3751 if (typeof(b.listeners) == 'undefined') {
3752 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3755 var btn = Roo.factory(b);
3757 btn.render(this.getButtonContainer());
3761 // render the children.
3764 if(typeof(this.items) != 'undefined'){
3765 var items = this.items;
3768 for(var i =0;i < items.length;i++) {
3769 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3773 this.items = nitems;
3775 // where are these used - they used to be body/close/footer
3779 //this.el.addClass([this.fieldClass, this.cls]);
3783 getAutoCreate : function()
3785 // we will default to modal-body-overflow - might need to remove or make optional later.
3787 cls : 'modal-body enable-modal-body-overflow ',
3788 html : this.html || ''
3793 cls : 'modal-title',
3797 if(this.specificTitle){
3803 if (this.allow_close && Roo.bootstrap.version == 3) {
3813 if (this.allow_close && Roo.bootstrap.version == 4) {
3823 if(this.size.length){
3824 size = 'modal-' + this.size;
3827 var footer = Roo.bootstrap.version == 3 ?
3829 cls : 'modal-footer',
3833 cls: 'btn-' + this.buttonPosition
3838 { // BS4 uses mr-auto on left buttons....
3839 cls : 'modal-footer'
3850 cls: "modal-dialog " + size,
3853 cls : "modal-content",
3856 cls : 'modal-header',
3871 modal.cls += ' fade';
3877 getChildContainer : function() {
3882 getButtonContainer : function() {
3884 return Roo.bootstrap.version == 4 ?
3885 this.el.select('.modal-footer',true).first()
3886 : this.el.select('.modal-footer div',true).first();
3889 initEvents : function()
3891 if (this.allow_close) {
3892 this.closeEl.on('click', this.hide, this);
3894 Roo.EventManager.onWindowResize(this.resize, this, true);
3902 this.maskEl.setSize(
3903 Roo.lib.Dom.getViewWidth(true),
3904 Roo.lib.Dom.getViewHeight(true)
3907 if (this.fitwindow) {
3911 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3912 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3917 if(this.max_width !== 0) {
3919 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3922 this.setSize(w, this.height);
3926 if(this.max_height) {
3927 this.setSize(w,Math.min(
3929 Roo.lib.Dom.getViewportHeight(true) - 60
3935 if(!this.fit_content) {
3936 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3940 this.setSize(w, Math.min(
3942 this.headerEl.getHeight() +
3943 this.footerEl.getHeight() +
3944 this.getChildHeight(this.bodyEl.dom.childNodes),
3945 Roo.lib.Dom.getViewportHeight(true) - 60)
3951 setSize : function(w,h)
3962 if (!this.rendered) {
3966 //this.el.setStyle('display', 'block');
3967 this.el.removeClass('hideing');
3968 this.el.dom.style.display='block';
3970 Roo.get(document.body).addClass('modal-open');
3972 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3975 this.el.addClass('show');
3976 this.el.addClass('in');
3979 this.el.addClass('show');
3980 this.el.addClass('in');
3983 // not sure how we can show data in here..
3985 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3988 Roo.get(document.body).addClass("x-body-masked");
3990 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3991 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3992 this.maskEl.dom.style.display = 'block';
3993 this.maskEl.addClass('show');
3998 this.fireEvent('show', this);
4000 // set zindex here - otherwise it appears to be ignored...
4001 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4004 this.items.forEach( function(e) {
4005 e.layout ? e.layout() : false;
4013 if(this.fireEvent("beforehide", this) !== false){
4015 this.maskEl.removeClass('show');
4017 this.maskEl.dom.style.display = '';
4018 Roo.get(document.body).removeClass("x-body-masked");
4019 this.el.removeClass('in');
4020 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4022 if(this.animate){ // why
4023 this.el.addClass('hideing');
4024 this.el.removeClass('show');
4026 if (!this.el.hasClass('hideing')) {
4027 return; // it's been shown again...
4030 this.el.dom.style.display='';
4032 Roo.get(document.body).removeClass('modal-open');
4033 this.el.removeClass('hideing');
4037 this.el.removeClass('show');
4038 this.el.dom.style.display='';
4039 Roo.get(document.body).removeClass('modal-open');
4042 this.fireEvent('hide', this);
4045 isVisible : function()
4048 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4052 addButton : function(str, cb)
4056 var b = Roo.apply({}, { html : str } );
4057 b.xns = b.xns || Roo.bootstrap;
4058 b.xtype = b.xtype || 'Button';
4059 if (typeof(b.listeners) == 'undefined') {
4060 b.listeners = { click : cb.createDelegate(this) };
4063 var btn = Roo.factory(b);
4065 btn.render(this.getButtonContainer());
4071 setDefaultButton : function(btn)
4073 //this.el.select('.modal-footer').()
4076 resizeTo: function(w,h)
4078 this.dialogEl.setWidth(w);
4080 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4082 this.bodyEl.setHeight(h - diff);
4084 this.fireEvent('resize', this);
4087 setContentSize : function(w, h)
4091 onButtonClick: function(btn,e)
4094 this.fireEvent('btnclick', btn.name, e);
4097 * Set the title of the Dialog
4098 * @param {String} str new Title
4100 setTitle: function(str) {
4101 this.titleEl.dom.innerHTML = str;
4104 * Set the body of the Dialog
4105 * @param {String} str new Title
4107 setBody: function(str) {
4108 this.bodyEl.dom.innerHTML = str;
4111 * Set the body of the Dialog using the template
4112 * @param {Obj} data - apply this data to the template and replace the body contents.
4114 applyBody: function(obj)
4117 Roo.log("Error - using apply Body without a template");
4120 this.tmpl.overwrite(this.bodyEl, obj);
4123 getChildHeight : function(child_nodes)
4127 child_nodes.length == 0
4132 var child_height = 0;
4134 for(var i = 0; i < child_nodes.length; i++) {
4137 * for modal with tabs...
4138 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4140 var layout_childs = child_nodes[i].childNodes;
4142 for(var j = 0; j < layout_childs.length; j++) {
4144 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4146 var layout_body_childs = layout_childs[j].childNodes;
4148 for(var k = 0; k < layout_body_childs.length; k++) {
4150 if(layout_body_childs[k].classList.contains('navbar')) {
4151 child_height += layout_body_childs[k].offsetHeight;
4155 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4157 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4159 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4161 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4162 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4177 child_height += child_nodes[i].offsetHeight;
4178 // Roo.log(child_nodes[i].offsetHeight);
4181 return child_height;
4187 Roo.apply(Roo.bootstrap.Modal, {
4189 * Button config that displays a single OK button
4198 * Button config that displays Yes and No buttons
4214 * Button config that displays OK and Cancel buttons
4229 * Button config that displays Yes, No and Cancel buttons
4253 * messagebox - can be used as a replace
4257 * @class Roo.MessageBox
4258 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4262 Roo.Msg.alert('Status', 'Changes saved successfully.');
4264 // Prompt for user data:
4265 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4267 // process text value...
4271 // Show a dialog using config options:
4273 title:'Save Changes?',
4274 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4275 buttons: Roo.Msg.YESNOCANCEL,
4282 Roo.bootstrap.MessageBox = function(){
4283 var dlg, opt, mask, waitTimer;
4284 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4285 var buttons, activeTextEl, bwidth;
4289 var handleButton = function(button){
4291 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4295 var handleHide = function(){
4297 dlg.el.removeClass(opt.cls);
4300 // Roo.TaskMgr.stop(waitTimer);
4301 // waitTimer = null;
4306 var updateButtons = function(b){
4309 buttons["ok"].hide();
4310 buttons["cancel"].hide();
4311 buttons["yes"].hide();
4312 buttons["no"].hide();
4313 dlg.footerEl.hide();
4317 dlg.footerEl.show();
4318 for(var k in buttons){
4319 if(typeof buttons[k] != "function"){
4322 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4323 width += buttons[k].el.getWidth()+15;
4333 var handleEsc = function(d, k, e){
4334 if(opt && opt.closable !== false){
4344 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4345 * @return {Roo.BasicDialog} The BasicDialog element
4347 getDialog : function(){
4349 dlg = new Roo.bootstrap.Modal( {
4352 //constraintoviewport:false,
4354 //collapsible : false,
4359 //buttonAlign:"center",
4360 closeClick : function(){
4361 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4364 handleButton("cancel");
4369 dlg.on("hide", handleHide);
4371 //dlg.addKeyListener(27, handleEsc);
4373 this.buttons = buttons;
4374 var bt = this.buttonText;
4375 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4376 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4377 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4378 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4380 bodyEl = dlg.bodyEl.createChild({
4382 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4383 '<textarea class="roo-mb-textarea"></textarea>' +
4384 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4386 msgEl = bodyEl.dom.firstChild;
4387 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4388 textboxEl.enableDisplayMode();
4389 textboxEl.addKeyListener([10,13], function(){
4390 if(dlg.isVisible() && opt && opt.buttons){
4393 }else if(opt.buttons.yes){
4394 handleButton("yes");
4398 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4399 textareaEl.enableDisplayMode();
4400 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4401 progressEl.enableDisplayMode();
4403 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4404 var pf = progressEl.dom.firstChild;
4406 pp = Roo.get(pf.firstChild);
4407 pp.setHeight(pf.offsetHeight);
4415 * Updates the message box body text
4416 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4417 * the XHTML-compliant non-breaking space character '&#160;')
4418 * @return {Roo.MessageBox} This message box
4420 updateText : function(text)
4422 if(!dlg.isVisible() && !opt.width){
4423 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4424 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4426 msgEl.innerHTML = text || ' ';
4428 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4429 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4431 Math.min(opt.width || cw , this.maxWidth),
4432 Math.max(opt.minWidth || this.minWidth, bwidth)
4435 activeTextEl.setWidth(w);
4437 if(dlg.isVisible()){
4438 dlg.fixedcenter = false;
4440 // to big, make it scroll. = But as usual stupid IE does not support
4443 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4444 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4445 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4447 bodyEl.dom.style.height = '';
4448 bodyEl.dom.style.overflowY = '';
4451 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4453 bodyEl.dom.style.overflowX = '';
4456 dlg.setContentSize(w, bodyEl.getHeight());
4457 if(dlg.isVisible()){
4458 dlg.fixedcenter = true;
4464 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4465 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4466 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4467 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4468 * @return {Roo.MessageBox} This message box
4470 updateProgress : function(value, text){
4472 this.updateText(text);
4475 if (pp) { // weird bug on my firefox - for some reason this is not defined
4476 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4477 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4483 * Returns true if the message box is currently displayed
4484 * @return {Boolean} True if the message box is visible, else false
4486 isVisible : function(){
4487 return dlg && dlg.isVisible();
4491 * Hides the message box if it is displayed
4494 if(this.isVisible()){
4500 * Displays a new message box, or reinitializes an existing message box, based on the config options
4501 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4502 * The following config object properties are supported:
4504 Property Type Description
4505 ---------- --------------- ------------------------------------------------------------------------------------
4506 animEl String/Element An id or Element from which the message box should animate as it opens and
4507 closes (defaults to undefined)
4508 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4509 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4510 closable Boolean False to hide the top-right close button (defaults to true). Note that
4511 progress and wait dialogs will ignore this property and always hide the
4512 close button as they can only be closed programmatically.
4513 cls String A custom CSS class to apply to the message box element
4514 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4515 displayed (defaults to 75)
4516 fn Function A callback function to execute after closing the dialog. The arguments to the
4517 function will be btn (the name of the button that was clicked, if applicable,
4518 e.g. "ok"), and text (the value of the active text field, if applicable).
4519 Progress and wait dialogs will ignore this option since they do not respond to
4520 user actions and can only be closed programmatically, so any required function
4521 should be called by the same code after it closes the dialog.
4522 icon String A CSS class that provides a background image to be used as an icon for
4523 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4524 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4525 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4526 modal Boolean False to allow user interaction with the page while the message box is
4527 displayed (defaults to true)
4528 msg String A string that will replace the existing message box body text (defaults
4529 to the XHTML-compliant non-breaking space character ' ')
4530 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4531 progress Boolean True to display a progress bar (defaults to false)
4532 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4533 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4534 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4535 title String The title text
4536 value String The string value to set into the active textbox element if displayed
4537 wait Boolean True to display a progress bar (defaults to false)
4538 width Number The width of the dialog in pixels
4545 msg: 'Please enter your address:',
4547 buttons: Roo.MessageBox.OKCANCEL,
4550 animEl: 'addAddressBtn'
4553 * @param {Object} config Configuration options
4554 * @return {Roo.MessageBox} This message box
4556 show : function(options)
4559 // this causes nightmares if you show one dialog after another
4560 // especially on callbacks..
4562 if(this.isVisible()){
4565 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4566 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4567 Roo.log("New Dialog Message:" + options.msg )
4568 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4569 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4572 var d = this.getDialog();
4574 d.setTitle(opt.title || " ");
4575 d.closeEl.setDisplayed(opt.closable !== false);
4576 activeTextEl = textboxEl;
4577 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4582 textareaEl.setHeight(typeof opt.multiline == "number" ?
4583 opt.multiline : this.defaultTextHeight);
4584 activeTextEl = textareaEl;
4593 progressEl.setDisplayed(opt.progress === true);
4595 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4597 this.updateProgress(0);
4598 activeTextEl.dom.value = opt.value || "";
4600 dlg.setDefaultButton(activeTextEl);
4602 var bs = opt.buttons;
4606 }else if(bs && bs.yes){
4607 db = buttons["yes"];
4609 dlg.setDefaultButton(db);
4611 bwidth = updateButtons(opt.buttons);
4612 this.updateText(opt.msg);
4614 d.el.addClass(opt.cls);
4616 d.proxyDrag = opt.proxyDrag === true;
4617 d.modal = opt.modal !== false;
4618 d.mask = opt.modal !== false ? mask : false;
4620 // force it to the end of the z-index stack so it gets a cursor in FF
4621 document.body.appendChild(dlg.el.dom);
4622 d.animateTarget = null;
4623 d.show(options.animEl);
4629 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4630 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4631 * and closing the message box when the process is complete.
4632 * @param {String} title The title bar text
4633 * @param {String} msg The message box body text
4634 * @return {Roo.MessageBox} This message box
4636 progress : function(title, msg){
4643 minWidth: this.minProgressWidth,
4650 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4651 * If a callback function is passed it will be called after the user clicks the button, and the
4652 * id of the button that was clicked will be passed as the only parameter to the callback
4653 * (could also be the top-right close button).
4654 * @param {String} title The title bar text
4655 * @param {String} msg The message box body text
4656 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4657 * @param {Object} scope (optional) The scope of the callback function
4658 * @return {Roo.MessageBox} This message box
4660 alert : function(title, msg, fn, scope)
4675 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4676 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4677 * You are responsible for closing the message box when the process is complete.
4678 * @param {String} msg The message box body text
4679 * @param {String} title (optional) The title bar text
4680 * @return {Roo.MessageBox} This message box
4682 wait : function(msg, title){
4693 waitTimer = Roo.TaskMgr.start({
4695 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4703 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4704 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4705 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4706 * @param {String} title The title bar text
4707 * @param {String} msg The message box body text
4708 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4709 * @param {Object} scope (optional) The scope of the callback function
4710 * @return {Roo.MessageBox} This message box
4712 confirm : function(title, msg, fn, scope){
4716 buttons: this.YESNO,
4725 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4726 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
4727 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4728 * (could also be the top-right close button) and the text that was entered will be passed as the two
4729 * parameters to the callback.
4730 * @param {String} title The title bar text
4731 * @param {String} msg The message box body text
4732 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4733 * @param {Object} scope (optional) The scope of the callback function
4734 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4735 * property, or the height in pixels to create the textbox (defaults to false / single-line)
4736 * @return {Roo.MessageBox} This message box
4738 prompt : function(title, msg, fn, scope, multiline){
4742 buttons: this.OKCANCEL,
4747 multiline: multiline,
4754 * Button config that displays a single OK button
4759 * Button config that displays Yes and No buttons
4762 YESNO : {yes:true, no:true},
4764 * Button config that displays OK and Cancel buttons
4767 OKCANCEL : {ok:true, cancel:true},
4769 * Button config that displays Yes, No and Cancel buttons
4772 YESNOCANCEL : {yes:true, no:true, cancel:true},
4775 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4778 defaultTextHeight : 75,
4780 * The maximum width in pixels of the message box (defaults to 600)
4785 * The minimum width in pixels of the message box (defaults to 100)
4790 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4791 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4794 minProgressWidth : 250,
4796 * An object containing the default button text strings that can be overriden for localized language support.
4797 * Supported properties are: ok, cancel, yes and no.
4798 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4811 * Shorthand for {@link Roo.MessageBox}
4813 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4814 Roo.Msg = Roo.Msg || Roo.MessageBox;
4823 * @class Roo.bootstrap.Navbar
4824 * @extends Roo.bootstrap.Component
4825 * Bootstrap Navbar class
4828 * Create a new Navbar
4829 * @param {Object} config The config object
4833 Roo.bootstrap.Navbar = function(config){
4834 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4838 * @event beforetoggle
4839 * Fire before toggle the menu
4840 * @param {Roo.EventObject} e
4842 "beforetoggle" : true
4846 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4855 getAutoCreate : function(){
4858 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4862 initEvents :function ()
4864 //Roo.log(this.el.select('.navbar-toggle',true));
4865 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4872 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4874 var size = this.el.getSize();
4875 this.maskEl.setSize(size.width, size.height);
4876 this.maskEl.enableDisplayMode("block");
4885 getChildContainer : function()
4887 if (this.el && this.el.select('.collapse').getCount()) {
4888 return this.el.select('.collapse',true).first();
4903 onToggle : function()
4906 if(this.fireEvent('beforetoggle', this) === false){
4909 var ce = this.el.select('.navbar-collapse',true).first();
4911 if (!ce.hasClass('show')) {
4921 * Expand the navbar pulldown
4923 expand : function ()
4926 var ce = this.el.select('.navbar-collapse',true).first();
4927 if (ce.hasClass('collapsing')) {
4930 ce.dom.style.height = '';
4932 ce.addClass('in'); // old...
4933 ce.removeClass('collapse');
4934 ce.addClass('show');
4935 var h = ce.getHeight();
4937 ce.removeClass('show');
4938 // at this point we should be able to see it..
4939 ce.addClass('collapsing');
4941 ce.setHeight(0); // resize it ...
4942 ce.on('transitionend', function() {
4943 //Roo.log('done transition');
4944 ce.removeClass('collapsing');
4945 ce.addClass('show');
4946 ce.removeClass('collapse');
4948 ce.dom.style.height = '';
4949 }, this, { single: true} );
4951 ce.dom.scrollTop = 0;
4954 * Collapse the navbar pulldown
4956 collapse : function()
4958 var ce = this.el.select('.navbar-collapse',true).first();
4960 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4961 // it's collapsed or collapsing..
4964 ce.removeClass('in'); // old...
4965 ce.setHeight(ce.getHeight());
4966 ce.removeClass('show');
4967 ce.addClass('collapsing');
4969 ce.on('transitionend', function() {
4970 ce.dom.style.height = '';
4971 ce.removeClass('collapsing');
4972 ce.addClass('collapse');
4973 }, this, { single: true} );
4993 * @class Roo.bootstrap.NavSimplebar
4994 * @extends Roo.bootstrap.Navbar
4995 * Bootstrap Sidebar class
4997 * @cfg {Boolean} inverse is inverted color
4999 * @cfg {String} type (nav | pills | tabs)
5000 * @cfg {Boolean} arrangement stacked | justified
5001 * @cfg {String} align (left | right) alignment
5003 * @cfg {Boolean} main (true|false) main nav bar? default false
5004 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5006 * @cfg {String} tag (header|footer|nav|div) default is nav
5008 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5012 * Create a new Sidebar
5013 * @param {Object} config The config object
5017 Roo.bootstrap.NavSimplebar = function(config){
5018 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5021 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5037 getAutoCreate : function(){
5041 tag : this.tag || 'div',
5042 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5044 if (['light','white'].indexOf(this.weight) > -1) {
5045 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5047 cfg.cls += ' bg-' + this.weight;
5050 cfg.cls += ' navbar-inverse';
5054 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5056 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5065 cls: 'nav nav-' + this.xtype,
5071 this.type = this.type || 'nav';
5072 if (['tabs','pills'].indexOf(this.type) != -1) {
5073 cfg.cn[0].cls += ' nav-' + this.type
5077 if (this.type!=='nav') {
5078 Roo.log('nav type must be nav/tabs/pills')
5080 cfg.cn[0].cls += ' navbar-nav'
5086 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5087 cfg.cn[0].cls += ' nav-' + this.arrangement;
5091 if (this.align === 'right') {
5092 cfg.cn[0].cls += ' navbar-right';
5117 * navbar-expand-md fixed-top
5121 * @class Roo.bootstrap.NavHeaderbar
5122 * @extends Roo.bootstrap.NavSimplebar
5123 * Bootstrap Sidebar class
5125 * @cfg {String} brand what is brand
5126 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5127 * @cfg {String} brand_href href of the brand
5128 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5129 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5130 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5131 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5134 * Create a new Sidebar
5135 * @param {Object} config The config object
5139 Roo.bootstrap.NavHeaderbar = function(config){
5140 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5144 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5151 desktopCenter : false,
5154 getAutoCreate : function(){
5157 tag: this.nav || 'nav',
5158 cls: 'navbar navbar-expand-md',
5164 if (this.desktopCenter) {
5165 cn.push({cls : 'container', cn : []});
5173 cls: 'navbar-toggle navbar-toggler',
5174 'data-toggle': 'collapse',
5179 html: 'Toggle navigation'
5183 cls: 'icon-bar navbar-toggler-icon'
5196 cn.push( Roo.bootstrap.version == 4 ? btn : {
5198 cls: 'navbar-header',
5207 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5211 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5213 if (['light','white'].indexOf(this.weight) > -1) {
5214 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5216 cfg.cls += ' bg-' + this.weight;
5219 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5220 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5222 // tag can override this..
5224 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5227 if (this.brand !== '') {
5228 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5229 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5231 href: this.brand_href ? this.brand_href : '#',
5232 cls: 'navbar-brand',
5240 cfg.cls += ' main-nav';
5248 getHeaderChildContainer : function()
5250 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5251 return this.el.select('.navbar-header',true).first();
5254 return this.getChildContainer();
5257 getChildContainer : function()
5260 return this.el.select('.roo-navbar-collapse',true).first();
5265 initEvents : function()
5267 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5269 if (this.autohide) {
5274 Roo.get(document).on('scroll',function(e) {
5275 var ns = Roo.get(document).getScroll().top;
5276 var os = prevScroll;
5280 ft.removeClass('slideDown');
5281 ft.addClass('slideUp');
5284 ft.removeClass('slideUp');
5285 ft.addClass('slideDown');
5306 * @class Roo.bootstrap.NavSidebar
5307 * @extends Roo.bootstrap.Navbar
5308 * Bootstrap Sidebar class
5311 * Create a new Sidebar
5312 * @param {Object} config The config object
5316 Roo.bootstrap.NavSidebar = function(config){
5317 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5320 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5322 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5324 getAutoCreate : function(){
5329 cls: 'sidebar sidebar-nav'
5351 * @class Roo.bootstrap.NavGroup
5352 * @extends Roo.bootstrap.Component
5353 * Bootstrap NavGroup class
5354 * @cfg {String} align (left|right)
5355 * @cfg {Boolean} inverse
5356 * @cfg {String} type (nav|pills|tab) default nav
5357 * @cfg {String} navId - reference Id for navbar.
5361 * Create a new nav group
5362 * @param {Object} config The config object
5365 Roo.bootstrap.NavGroup = function(config){
5366 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5369 Roo.bootstrap.NavGroup.register(this);
5373 * Fires when the active item changes
5374 * @param {Roo.bootstrap.NavGroup} this
5375 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5376 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5383 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5394 getAutoCreate : function()
5396 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5402 if (Roo.bootstrap.version == 4) {
5403 if (['tabs','pills'].indexOf(this.type) != -1) {
5404 cfg.cls += ' nav-' + this.type;
5406 // trying to remove so header bar can right align top?
5407 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5408 // do not use on header bar...
5409 cfg.cls += ' navbar-nav';
5414 if (['tabs','pills'].indexOf(this.type) != -1) {
5415 cfg.cls += ' nav-' + this.type
5417 if (this.type !== 'nav') {
5418 Roo.log('nav type must be nav/tabs/pills')
5420 cfg.cls += ' navbar-nav'
5424 if (this.parent() && this.parent().sidebar) {
5427 cls: 'dashboard-menu sidebar-menu'
5433 if (this.form === true) {
5436 cls: 'navbar-form form-inline'
5438 //nav navbar-right ml-md-auto
5439 if (this.align === 'right') {
5440 cfg.cls += ' navbar-right ml-md-auto';
5442 cfg.cls += ' navbar-left';
5446 if (this.align === 'right') {
5447 cfg.cls += ' navbar-right ml-md-auto';
5449 cfg.cls += ' mr-auto';
5453 cfg.cls += ' navbar-inverse';
5461 * sets the active Navigation item
5462 * @param {Roo.bootstrap.NavItem} the new current navitem
5464 setActiveItem : function(item)
5467 Roo.each(this.navItems, function(v){
5472 v.setActive(false, true);
5479 item.setActive(true, true);
5480 this.fireEvent('changed', this, item, prev);
5485 * gets the active Navigation item
5486 * @return {Roo.bootstrap.NavItem} the current navitem
5488 getActive : function()
5492 Roo.each(this.navItems, function(v){
5503 indexOfNav : function()
5507 Roo.each(this.navItems, function(v,i){
5518 * adds a Navigation item
5519 * @param {Roo.bootstrap.NavItem} the navitem to add
5521 addItem : function(cfg)
5523 if (this.form && Roo.bootstrap.version == 4) {
5526 var cn = new Roo.bootstrap.NavItem(cfg);
5528 cn.parentId = this.id;
5529 cn.onRender(this.el, null);
5533 * register a Navigation item
5534 * @param {Roo.bootstrap.NavItem} the navitem to add
5536 register : function(item)
5538 this.navItems.push( item);
5539 item.navId = this.navId;
5544 * clear all the Navigation item
5547 clearAll : function()
5550 this.el.dom.innerHTML = '';
5553 getNavItem: function(tabId)
5556 Roo.each(this.navItems, function(e) {
5557 if (e.tabId == tabId) {
5567 setActiveNext : function()
5569 var i = this.indexOfNav(this.getActive());
5570 if (i > this.navItems.length) {
5573 this.setActiveItem(this.navItems[i+1]);
5575 setActivePrev : function()
5577 var i = this.indexOfNav(this.getActive());
5581 this.setActiveItem(this.navItems[i-1]);
5583 clearWasActive : function(except) {
5584 Roo.each(this.navItems, function(e) {
5585 if (e.tabId != except.tabId && e.was_active) {
5586 e.was_active = false;
5593 getWasActive : function ()
5596 Roo.each(this.navItems, function(e) {
5611 Roo.apply(Roo.bootstrap.NavGroup, {
5615 * register a Navigation Group
5616 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5618 register : function(navgrp)
5620 this.groups[navgrp.navId] = navgrp;
5624 * fetch a Navigation Group based on the navigation ID
5625 * @param {string} the navgroup to add
5626 * @returns {Roo.bootstrap.NavGroup} the navgroup
5628 get: function(navId) {
5629 if (typeof(this.groups[navId]) == 'undefined') {
5631 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5633 return this.groups[navId] ;
5648 * @class Roo.bootstrap.NavItem
5649 * @extends Roo.bootstrap.Component
5650 * Bootstrap Navbar.NavItem class
5651 * @cfg {String} href link to
5652 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5654 * @cfg {String} html content of button
5655 * @cfg {String} badge text inside badge
5656 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5657 * @cfg {String} glyphicon DEPRICATED - use fa
5658 * @cfg {String} icon DEPRICATED - use fa
5659 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5660 * @cfg {Boolean} active Is item active
5661 * @cfg {Boolean} disabled Is item disabled
5663 * @cfg {Boolean} preventDefault (true | false) default false
5664 * @cfg {String} tabId the tab that this item activates.
5665 * @cfg {String} tagtype (a|span) render as a href or span?
5666 * @cfg {Boolean} animateRef (true|false) link to element default false
5669 * Create a new Navbar Item
5670 * @param {Object} config The config object
5672 Roo.bootstrap.NavItem = function(config){
5673 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5678 * The raw click event for the entire grid.
5679 * @param {Roo.EventObject} e
5684 * Fires when the active item active state changes
5685 * @param {Roo.bootstrap.NavItem} this
5686 * @param {boolean} state the new state
5692 * Fires when scroll to element
5693 * @param {Roo.bootstrap.NavItem} this
5694 * @param {Object} options
5695 * @param {Roo.EventObject} e
5703 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5712 preventDefault : false,
5720 button_outline : false,
5724 getAutoCreate : function(){
5732 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5734 if (this.disabled) {
5735 cfg.cls += ' disabled';
5739 if (this.button_weight.length) {
5740 cfg.tag = this.href ? 'a' : 'button';
5741 cfg.html = this.html || '';
5742 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5744 cfg.href = this.href;
5747 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5750 // menu .. should add dropdown-menu class - so no need for carat..
5752 if (this.badge !== '') {
5754 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5759 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5763 href : this.href || "#",
5764 html: this.html || ''
5767 if (this.tagtype == 'a') {
5768 cfg.cn[0].cls = 'nav-link';
5771 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5774 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5776 if(this.glyphicon) {
5777 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5782 cfg.cn[0].html += " <span class='caret'></span>";
5786 if (this.badge !== '') {
5788 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5796 onRender : function(ct, position)
5798 // Roo.log("Call onRender: " + this.xtype);
5799 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5803 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5804 this.navLink = this.el.select('.nav-link',true).first();
5809 initEvents: function()
5811 if (typeof (this.menu) != 'undefined') {
5812 this.menu.parentType = this.xtype;
5813 this.menu.triggerEl = this.el;
5814 this.menu = this.addxtype(Roo.apply({}, this.menu));
5817 this.el.select('a',true).on('click', this.onClick, this);
5819 if(this.tagtype == 'span'){
5820 this.el.select('span',true).on('click', this.onClick, this);
5823 // at this point parent should be available..
5824 this.parent().register(this);
5827 onClick : function(e)
5829 if (e.getTarget('.dropdown-menu-item')) {
5830 // did you click on a menu itemm.... - then don't trigger onclick..
5835 this.preventDefault ||
5838 Roo.log("NavItem - prevent Default?");
5842 if (this.disabled) {
5846 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5847 if (tg && tg.transition) {
5848 Roo.log("waiting for the transitionend");
5854 //Roo.log("fire event clicked");
5855 if(this.fireEvent('click', this, e) === false){
5859 if(this.tagtype == 'span'){
5863 //Roo.log(this.href);
5864 var ael = this.el.select('a',true).first();
5867 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5868 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5869 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5870 return; // ignore... - it's a 'hash' to another page.
5872 Roo.log("NavItem - prevent Default?");
5874 this.scrollToElement(e);
5878 var p = this.parent();
5880 if (['tabs','pills'].indexOf(p.type)!==-1) {
5881 if (typeof(p.setActiveItem) !== 'undefined') {
5882 p.setActiveItem(this);
5886 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5887 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5888 // remove the collapsed menu expand...
5889 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5893 isActive: function () {
5896 setActive : function(state, fire, is_was_active)
5898 if (this.active && !state && this.navId) {
5899 this.was_active = true;
5900 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5902 nv.clearWasActive(this);
5906 this.active = state;
5909 this.el.removeClass('active');
5910 this.navLink ? this.navLink.removeClass('active') : false;
5911 } else if (!this.el.hasClass('active')) {
5913 this.el.addClass('active');
5914 if (Roo.bootstrap.version == 4 && this.navLink ) {
5915 this.navLink.addClass('active');
5920 this.fireEvent('changed', this, state);
5923 // show a panel if it's registered and related..
5925 if (!this.navId || !this.tabId || !state || is_was_active) {
5929 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5933 var pan = tg.getPanelByName(this.tabId);
5937 // if we can not flip to new panel - go back to old nav highlight..
5938 if (false == tg.showPanel(pan)) {
5939 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5941 var onav = nv.getWasActive();
5943 onav.setActive(true, false, true);
5952 // this should not be here...
5953 setDisabled : function(state)
5955 this.disabled = state;
5957 this.el.removeClass('disabled');
5958 } else if (!this.el.hasClass('disabled')) {
5959 this.el.addClass('disabled');
5965 * Fetch the element to display the tooltip on.
5966 * @return {Roo.Element} defaults to this.el
5968 tooltipEl : function()
5970 return this.el.select('' + this.tagtype + '', true).first();
5973 scrollToElement : function(e)
5975 var c = document.body;
5978 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5980 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5981 c = document.documentElement;
5984 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5990 var o = target.calcOffsetsTo(c);
5997 this.fireEvent('scrollto', this, options, e);
5999 Roo.get(c).scrollTo('top', options.value, true);
6012 * <span> icon </span>
6013 * <span> text </span>
6014 * <span>badge </span>
6018 * @class Roo.bootstrap.NavSidebarItem
6019 * @extends Roo.bootstrap.NavItem
6020 * Bootstrap Navbar.NavSidebarItem class
6021 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6022 * {Boolean} open is the menu open
6023 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6024 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6025 * {String} buttonSize (sm|md|lg)the extra classes for the button
6026 * {Boolean} showArrow show arrow next to the text (default true)
6028 * Create a new Navbar Button
6029 * @param {Object} config The config object
6031 Roo.bootstrap.NavSidebarItem = function(config){
6032 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6037 * The raw click event for the entire grid.
6038 * @param {Roo.EventObject} e
6043 * Fires when the active item active state changes
6044 * @param {Roo.bootstrap.NavSidebarItem} this
6045 * @param {boolean} state the new state
6053 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6055 badgeWeight : 'default',
6061 buttonWeight : 'default',
6067 getAutoCreate : function(){
6072 href : this.href || '#',
6078 if(this.buttonView){
6081 href : this.href || '#',
6082 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6095 cfg.cls += ' active';
6098 if (this.disabled) {
6099 cfg.cls += ' disabled';
6102 cfg.cls += ' open x-open';
6105 if (this.glyphicon || this.icon) {
6106 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6107 a.cn.push({ tag : 'i', cls : c }) ;
6110 if(!this.buttonView){
6113 html : this.html || ''
6120 if (this.badge !== '') {
6121 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6127 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6130 a.cls += ' dropdown-toggle treeview' ;
6136 initEvents : function()
6138 if (typeof (this.menu) != 'undefined') {
6139 this.menu.parentType = this.xtype;
6140 this.menu.triggerEl = this.el;
6141 this.menu = this.addxtype(Roo.apply({}, this.menu));
6144 this.el.on('click', this.onClick, this);
6146 if(this.badge !== ''){
6147 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6152 onClick : function(e)
6159 if(this.preventDefault){
6163 this.fireEvent('click', this, e);
6166 disable : function()
6168 this.setDisabled(true);
6173 this.setDisabled(false);
6176 setDisabled : function(state)
6178 if(this.disabled == state){
6182 this.disabled = state;
6185 this.el.addClass('disabled');
6189 this.el.removeClass('disabled');
6194 setActive : function(state)
6196 if(this.active == state){
6200 this.active = state;
6203 this.el.addClass('active');
6207 this.el.removeClass('active');
6212 isActive: function ()
6217 setBadge : function(str)
6223 this.badgeEl.dom.innerHTML = str;
6240 * @class Roo.bootstrap.Row
6241 * @extends Roo.bootstrap.Component
6242 * Bootstrap Row class (contains columns...)
6246 * @param {Object} config The config object
6249 Roo.bootstrap.Row = function(config){
6250 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6253 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6255 getAutoCreate : function(){
6274 * @class Roo.bootstrap.Pagination
6275 * @extends Roo.bootstrap.Component
6276 * Bootstrap Pagination class
6277 * @cfg {String} size xs | sm | md | lg
6278 * @cfg {Boolean} inverse false | true
6281 * Create a new Pagination
6282 * @param {Object} config The config object
6285 Roo.bootstrap.Pagination = function(config){
6286 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6289 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6295 getAutoCreate : function(){
6301 cfg.cls += ' inverse';
6307 cfg.cls += " " + this.cls;
6325 * @class Roo.bootstrap.PaginationItem
6326 * @extends Roo.bootstrap.Component
6327 * Bootstrap PaginationItem class
6328 * @cfg {String} html text
6329 * @cfg {String} href the link
6330 * @cfg {Boolean} preventDefault (true | false) default true
6331 * @cfg {Boolean} active (true | false) default false
6332 * @cfg {Boolean} disabled default false
6336 * Create a new PaginationItem
6337 * @param {Object} config The config object
6341 Roo.bootstrap.PaginationItem = function(config){
6342 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6347 * The raw click event for the entire grid.
6348 * @param {Roo.EventObject} e
6354 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6358 preventDefault: true,
6363 getAutoCreate : function(){
6369 href : this.href ? this.href : '#',
6370 html : this.html ? this.html : ''
6380 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6384 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6390 initEvents: function() {
6392 this.el.on('click', this.onClick, this);
6395 onClick : function(e)
6397 Roo.log('PaginationItem on click ');
6398 if(this.preventDefault){
6406 this.fireEvent('click', this, e);
6422 * @class Roo.bootstrap.Slider
6423 * @extends Roo.bootstrap.Component
6424 * Bootstrap Slider class
6427 * Create a new Slider
6428 * @param {Object} config The config object
6431 Roo.bootstrap.Slider = function(config){
6432 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6435 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6437 getAutoCreate : function(){
6441 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6445 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6457 * Ext JS Library 1.1.1
6458 * Copyright(c) 2006-2007, Ext JS, LLC.
6460 * Originally Released Under LGPL - original licence link has changed is not relivant.
6463 * <script type="text/javascript">
6468 * @class Roo.grid.ColumnModel
6469 * @extends Roo.util.Observable
6470 * This is the default implementation of a ColumnModel used by the Grid. It defines
6471 * the columns in the grid.
6474 var colModel = new Roo.grid.ColumnModel([
6475 {header: "Ticker", width: 60, sortable: true, locked: true},
6476 {header: "Company Name", width: 150, sortable: true},
6477 {header: "Market Cap.", width: 100, sortable: true},
6478 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6479 {header: "Employees", width: 100, sortable: true, resizable: false}
6484 * The config options listed for this class are options which may appear in each
6485 * individual column definition.
6486 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6488 * @param {Object} config An Array of column config objects. See this class's
6489 * config objects for details.
6491 Roo.grid.ColumnModel = function(config){
6493 * The config passed into the constructor
6495 this.config = config;
6498 // if no id, create one
6499 // if the column does not have a dataIndex mapping,
6500 // map it to the order it is in the config
6501 for(var i = 0, len = config.length; i < len; i++){
6503 if(typeof c.dataIndex == "undefined"){
6506 if(typeof c.renderer == "string"){
6507 c.renderer = Roo.util.Format[c.renderer];
6509 if(typeof c.id == "undefined"){
6512 if(c.editor && c.editor.xtype){
6513 c.editor = Roo.factory(c.editor, Roo.grid);
6515 if(c.editor && c.editor.isFormField){
6516 c.editor = new Roo.grid.GridEditor(c.editor);
6518 this.lookup[c.id] = c;
6522 * The width of columns which have no width specified (defaults to 100)
6525 this.defaultWidth = 100;
6528 * Default sortable of columns which have no sortable specified (defaults to false)
6531 this.defaultSortable = false;
6535 * @event widthchange
6536 * Fires when the width of a column changes.
6537 * @param {ColumnModel} this
6538 * @param {Number} columnIndex The column index
6539 * @param {Number} newWidth The new width
6541 "widthchange": true,
6543 * @event headerchange
6544 * Fires when the text of a header changes.
6545 * @param {ColumnModel} this
6546 * @param {Number} columnIndex The column index
6547 * @param {Number} newText The new header text
6549 "headerchange": true,
6551 * @event hiddenchange
6552 * Fires when a column is hidden or "unhidden".
6553 * @param {ColumnModel} this
6554 * @param {Number} columnIndex The column index
6555 * @param {Boolean} hidden true if hidden, false otherwise
6557 "hiddenchange": true,
6559 * @event columnmoved
6560 * Fires when a column is moved.
6561 * @param {ColumnModel} this
6562 * @param {Number} oldIndex
6563 * @param {Number} newIndex
6565 "columnmoved" : true,
6567 * @event columlockchange
6568 * Fires when a column's locked state is changed
6569 * @param {ColumnModel} this
6570 * @param {Number} colIndex
6571 * @param {Boolean} locked true if locked
6573 "columnlockchange" : true
6575 Roo.grid.ColumnModel.superclass.constructor.call(this);
6577 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6579 * @cfg {String} header The header text to display in the Grid view.
6582 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6583 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6584 * specified, the column's index is used as an index into the Record's data Array.
6587 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6588 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6591 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6592 * Defaults to the value of the {@link #defaultSortable} property.
6593 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6596 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6599 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6602 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6605 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6608 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6609 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6610 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6611 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6614 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6617 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6620 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6623 * @cfg {String} cursor (Optional)
6626 * @cfg {String} tooltip (Optional)
6629 * @cfg {Number} xs (Optional)
6632 * @cfg {Number} sm (Optional)
6635 * @cfg {Number} md (Optional)
6638 * @cfg {Number} lg (Optional)
6641 * Returns the id of the column at the specified index.
6642 * @param {Number} index The column index
6643 * @return {String} the id
6645 getColumnId : function(index){
6646 return this.config[index].id;
6650 * Returns the column for a specified id.
6651 * @param {String} id The column id
6652 * @return {Object} the column
6654 getColumnById : function(id){
6655 return this.lookup[id];
6660 * Returns the column for a specified dataIndex.
6661 * @param {String} dataIndex The column dataIndex
6662 * @return {Object|Boolean} the column or false if not found
6664 getColumnByDataIndex: function(dataIndex){
6665 var index = this.findColumnIndex(dataIndex);
6666 return index > -1 ? this.config[index] : false;
6670 * Returns the index for a specified column id.
6671 * @param {String} id The column id
6672 * @return {Number} the index, or -1 if not found
6674 getIndexById : function(id){
6675 for(var i = 0, len = this.config.length; i < len; i++){
6676 if(this.config[i].id == id){
6684 * Returns the index for a specified column dataIndex.
6685 * @param {String} dataIndex The column dataIndex
6686 * @return {Number} the index, or -1 if not found
6689 findColumnIndex : function(dataIndex){
6690 for(var i = 0, len = this.config.length; i < len; i++){
6691 if(this.config[i].dataIndex == dataIndex){
6699 moveColumn : function(oldIndex, newIndex){
6700 var c = this.config[oldIndex];
6701 this.config.splice(oldIndex, 1);
6702 this.config.splice(newIndex, 0, c);
6703 this.dataMap = null;
6704 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6707 isLocked : function(colIndex){
6708 return this.config[colIndex].locked === true;
6711 setLocked : function(colIndex, value, suppressEvent){
6712 if(this.isLocked(colIndex) == value){
6715 this.config[colIndex].locked = value;
6717 this.fireEvent("columnlockchange", this, colIndex, value);
6721 getTotalLockedWidth : function(){
6723 for(var i = 0; i < this.config.length; i++){
6724 if(this.isLocked(i) && !this.isHidden(i)){
6725 this.totalWidth += this.getColumnWidth(i);
6731 getLockedCount : function(){
6732 for(var i = 0, len = this.config.length; i < len; i++){
6733 if(!this.isLocked(i)){
6738 return this.config.length;
6742 * Returns the number of columns.
6745 getColumnCount : function(visibleOnly){
6746 if(visibleOnly === true){
6748 for(var i = 0, len = this.config.length; i < len; i++){
6749 if(!this.isHidden(i)){
6755 return this.config.length;
6759 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6760 * @param {Function} fn
6761 * @param {Object} scope (optional)
6762 * @return {Array} result
6764 getColumnsBy : function(fn, scope){
6766 for(var i = 0, len = this.config.length; i < len; i++){
6767 var c = this.config[i];
6768 if(fn.call(scope||this, c, i) === true){
6776 * Returns true if the specified column is sortable.
6777 * @param {Number} col The column index
6780 isSortable : function(col){
6781 if(typeof this.config[col].sortable == "undefined"){
6782 return this.defaultSortable;
6784 return this.config[col].sortable;
6788 * Returns the rendering (formatting) function defined for the column.
6789 * @param {Number} col The column index.
6790 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6792 getRenderer : function(col){
6793 if(!this.config[col].renderer){
6794 return Roo.grid.ColumnModel.defaultRenderer;
6796 return this.config[col].renderer;
6800 * Sets the rendering (formatting) function for a column.
6801 * @param {Number} col The column index
6802 * @param {Function} fn The function to use to process the cell's raw data
6803 * to return HTML markup for the grid view. The render function is called with
6804 * the following parameters:<ul>
6805 * <li>Data value.</li>
6806 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6807 * <li>css A CSS style string to apply to the table cell.</li>
6808 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6809 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6810 * <li>Row index</li>
6811 * <li>Column index</li>
6812 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6814 setRenderer : function(col, fn){
6815 this.config[col].renderer = fn;
6819 * Returns the width for the specified column.
6820 * @param {Number} col The column index
6823 getColumnWidth : function(col){
6824 return this.config[col].width * 1 || this.defaultWidth;
6828 * Sets the width for a column.
6829 * @param {Number} col The column index
6830 * @param {Number} width The new width
6832 setColumnWidth : function(col, width, suppressEvent){
6833 this.config[col].width = width;
6834 this.totalWidth = null;
6836 this.fireEvent("widthchange", this, col, width);
6841 * Returns the total width of all columns.
6842 * @param {Boolean} includeHidden True to include hidden column widths
6845 getTotalWidth : function(includeHidden){
6846 if(!this.totalWidth){
6847 this.totalWidth = 0;
6848 for(var i = 0, len = this.config.length; i < len; i++){
6849 if(includeHidden || !this.isHidden(i)){
6850 this.totalWidth += this.getColumnWidth(i);
6854 return this.totalWidth;
6858 * Returns the header for the specified column.
6859 * @param {Number} col The column index
6862 getColumnHeader : function(col){
6863 return this.config[col].header;
6867 * Sets the header for a column.
6868 * @param {Number} col The column index
6869 * @param {String} header The new header
6871 setColumnHeader : function(col, header){
6872 this.config[col].header = header;
6873 this.fireEvent("headerchange", this, col, header);
6877 * Returns the tooltip for the specified column.
6878 * @param {Number} col The column index
6881 getColumnTooltip : function(col){
6882 return this.config[col].tooltip;
6885 * Sets the tooltip for a column.
6886 * @param {Number} col The column index
6887 * @param {String} tooltip The new tooltip
6889 setColumnTooltip : function(col, tooltip){
6890 this.config[col].tooltip = tooltip;
6894 * Returns the dataIndex for the specified column.
6895 * @param {Number} col The column index
6898 getDataIndex : function(col){
6899 return this.config[col].dataIndex;
6903 * Sets the dataIndex for a column.
6904 * @param {Number} col The column index
6905 * @param {Number} dataIndex The new dataIndex
6907 setDataIndex : function(col, dataIndex){
6908 this.config[col].dataIndex = dataIndex;
6914 * Returns true if the cell is editable.
6915 * @param {Number} colIndex The column index
6916 * @param {Number} rowIndex The row index - this is nto actually used..?
6919 isCellEditable : function(colIndex, rowIndex){
6920 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6924 * Returns the editor defined for the cell/column.
6925 * return false or null to disable editing.
6926 * @param {Number} colIndex The column index
6927 * @param {Number} rowIndex The row index
6930 getCellEditor : function(colIndex, rowIndex){
6931 return this.config[colIndex].editor;
6935 * Sets if a column is editable.
6936 * @param {Number} col The column index
6937 * @param {Boolean} editable True if the column is editable
6939 setEditable : function(col, editable){
6940 this.config[col].editable = editable;
6945 * Returns true if the column is hidden.
6946 * @param {Number} colIndex The column index
6949 isHidden : function(colIndex){
6950 return this.config[colIndex].hidden;
6955 * Returns true if the column width cannot be changed
6957 isFixed : function(colIndex){
6958 return this.config[colIndex].fixed;
6962 * Returns true if the column can be resized
6965 isResizable : function(colIndex){
6966 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6969 * Sets if a column is hidden.
6970 * @param {Number} colIndex The column index
6971 * @param {Boolean} hidden True if the column is hidden
6973 setHidden : function(colIndex, hidden){
6974 this.config[colIndex].hidden = hidden;
6975 this.totalWidth = null;
6976 this.fireEvent("hiddenchange", this, colIndex, hidden);
6980 * Sets the editor for a column.
6981 * @param {Number} col The column index
6982 * @param {Object} editor The editor object
6984 setEditor : function(col, editor){
6985 this.config[col].editor = editor;
6989 Roo.grid.ColumnModel.defaultRenderer = function(value)
6991 if(typeof value == "object") {
6994 if(typeof value == "string" && value.length < 1){
6998 return String.format("{0}", value);
7001 // Alias for backwards compatibility
7002 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7005 * Ext JS Library 1.1.1
7006 * Copyright(c) 2006-2007, Ext JS, LLC.
7008 * Originally Released Under LGPL - original licence link has changed is not relivant.
7011 * <script type="text/javascript">
7015 * @class Roo.LoadMask
7016 * A simple utility class for generically masking elements while loading data. If the element being masked has
7017 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7018 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7019 * element's UpdateManager load indicator and will be destroyed after the initial load.
7021 * Create a new LoadMask
7022 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7023 * @param {Object} config The config object
7025 Roo.LoadMask = function(el, config){
7026 this.el = Roo.get(el);
7027 Roo.apply(this, config);
7029 this.store.on('beforeload', this.onBeforeLoad, this);
7030 this.store.on('load', this.onLoad, this);
7031 this.store.on('loadexception', this.onLoadException, this);
7032 this.removeMask = false;
7034 var um = this.el.getUpdateManager();
7035 um.showLoadIndicator = false; // disable the default indicator
7036 um.on('beforeupdate', this.onBeforeLoad, this);
7037 um.on('update', this.onLoad, this);
7038 um.on('failure', this.onLoad, this);
7039 this.removeMask = true;
7043 Roo.LoadMask.prototype = {
7045 * @cfg {Boolean} removeMask
7046 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7047 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7051 * The text to display in a centered loading message box (defaults to 'Loading...')
7055 * @cfg {String} msgCls
7056 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7058 msgCls : 'x-mask-loading',
7061 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7067 * Disables the mask to prevent it from being displayed
7069 disable : function(){
7070 this.disabled = true;
7074 * Enables the mask so that it can be displayed
7076 enable : function(){
7077 this.disabled = false;
7080 onLoadException : function()
7084 if (typeof(arguments[3]) != 'undefined') {
7085 Roo.MessageBox.alert("Error loading",arguments[3]);
7089 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7090 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7097 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7102 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7106 onBeforeLoad : function(){
7108 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7113 destroy : function(){
7115 this.store.un('beforeload', this.onBeforeLoad, this);
7116 this.store.un('load', this.onLoad, this);
7117 this.store.un('loadexception', this.onLoadException, this);
7119 var um = this.el.getUpdateManager();
7120 um.un('beforeupdate', this.onBeforeLoad, this);
7121 um.un('update', this.onLoad, this);
7122 um.un('failure', this.onLoad, this);
7133 * @class Roo.bootstrap.Table
7134 * @extends Roo.bootstrap.Component
7135 * Bootstrap Table class
7136 * @cfg {String} cls table class
7137 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7138 * @cfg {String} bgcolor Specifies the background color for a table
7139 * @cfg {Number} border Specifies whether the table cells should have borders or not
7140 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7141 * @cfg {Number} cellspacing Specifies the space between cells
7142 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7143 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7144 * @cfg {String} sortable Specifies that the table should be sortable
7145 * @cfg {String} summary Specifies a summary of the content of a table
7146 * @cfg {Number} width Specifies the width of a table
7147 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7149 * @cfg {boolean} striped Should the rows be alternative striped
7150 * @cfg {boolean} bordered Add borders to the table
7151 * @cfg {boolean} hover Add hover highlighting
7152 * @cfg {boolean} condensed Format condensed
7153 * @cfg {boolean} responsive Format condensed
7154 * @cfg {Boolean} loadMask (true|false) default false
7155 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7156 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7157 * @cfg {Boolean} rowSelection (true|false) default false
7158 * @cfg {Boolean} cellSelection (true|false) default false
7159 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7160 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7161 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7162 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7166 * Create a new Table
7167 * @param {Object} config The config object
7170 Roo.bootstrap.Table = function(config){
7171 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7176 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7177 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7178 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7179 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7181 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7183 this.sm.grid = this;
7184 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7185 this.sm = this.selModel;
7186 this.sm.xmodule = this.xmodule || false;
7189 if (this.cm && typeof(this.cm.config) == 'undefined') {
7190 this.colModel = new Roo.grid.ColumnModel(this.cm);
7191 this.cm = this.colModel;
7192 this.cm.xmodule = this.xmodule || false;
7195 this.store= Roo.factory(this.store, Roo.data);
7196 this.ds = this.store;
7197 this.ds.xmodule = this.xmodule || false;
7200 if (this.footer && this.store) {
7201 this.footer.dataSource = this.ds;
7202 this.footer = Roo.factory(this.footer);
7209 * Fires when a cell is clicked
7210 * @param {Roo.bootstrap.Table} this
7211 * @param {Roo.Element} el
7212 * @param {Number} rowIndex
7213 * @param {Number} columnIndex
7214 * @param {Roo.EventObject} e
7218 * @event celldblclick
7219 * Fires when a cell is double clicked
7220 * @param {Roo.bootstrap.Table} this
7221 * @param {Roo.Element} el
7222 * @param {Number} rowIndex
7223 * @param {Number} columnIndex
7224 * @param {Roo.EventObject} e
7226 "celldblclick" : true,
7229 * Fires when a row is clicked
7230 * @param {Roo.bootstrap.Table} this
7231 * @param {Roo.Element} el
7232 * @param {Number} rowIndex
7233 * @param {Roo.EventObject} e
7237 * @event rowdblclick
7238 * Fires when a row is double clicked
7239 * @param {Roo.bootstrap.Table} this
7240 * @param {Roo.Element} el
7241 * @param {Number} rowIndex
7242 * @param {Roo.EventObject} e
7244 "rowdblclick" : true,
7247 * Fires when a mouseover occur
7248 * @param {Roo.bootstrap.Table} this
7249 * @param {Roo.Element} el
7250 * @param {Number} rowIndex
7251 * @param {Number} columnIndex
7252 * @param {Roo.EventObject} e
7257 * Fires when a mouseout occur
7258 * @param {Roo.bootstrap.Table} this
7259 * @param {Roo.Element} el
7260 * @param {Number} rowIndex
7261 * @param {Number} columnIndex
7262 * @param {Roo.EventObject} e
7267 * Fires when a row is rendered, so you can change add a style to it.
7268 * @param {Roo.bootstrap.Table} this
7269 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7273 * @event rowsrendered
7274 * Fires when all the rows have been rendered
7275 * @param {Roo.bootstrap.Table} this
7277 'rowsrendered' : true,
7279 * @event contextmenu
7280 * The raw contextmenu event for the entire grid.
7281 * @param {Roo.EventObject} e
7283 "contextmenu" : true,
7285 * @event rowcontextmenu
7286 * Fires when a row is right clicked
7287 * @param {Roo.bootstrap.Table} this
7288 * @param {Number} rowIndex
7289 * @param {Roo.EventObject} e
7291 "rowcontextmenu" : true,
7293 * @event cellcontextmenu
7294 * Fires when a cell is right clicked
7295 * @param {Roo.bootstrap.Table} this
7296 * @param {Number} rowIndex
7297 * @param {Number} cellIndex
7298 * @param {Roo.EventObject} e
7300 "cellcontextmenu" : true,
7302 * @event headercontextmenu
7303 * Fires when a header is right clicked
7304 * @param {Roo.bootstrap.Table} this
7305 * @param {Number} columnIndex
7306 * @param {Roo.EventObject} e
7308 "headercontextmenu" : true
7312 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7338 rowSelection : false,
7339 cellSelection : false,
7342 // Roo.Element - the tbody
7344 // Roo.Element - thead element
7347 container: false, // used by gridpanel...
7353 auto_hide_footer : false,
7355 getAutoCreate : function()
7357 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7364 if (this.scrollBody) {
7365 cfg.cls += ' table-body-fixed';
7368 cfg.cls += ' table-striped';
7372 cfg.cls += ' table-hover';
7374 if (this.bordered) {
7375 cfg.cls += ' table-bordered';
7377 if (this.condensed) {
7378 cfg.cls += ' table-condensed';
7380 if (this.responsive) {
7381 cfg.cls += ' table-responsive';
7385 cfg.cls+= ' ' +this.cls;
7388 // this lot should be simplifed...
7401 ].forEach(function(k) {
7409 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7412 if(this.store || this.cm){
7413 if(this.headerShow){
7414 cfg.cn.push(this.renderHeader());
7417 cfg.cn.push(this.renderBody());
7419 if(this.footerShow){
7420 cfg.cn.push(this.renderFooter());
7422 // where does this come from?
7423 //cfg.cls+= ' TableGrid';
7426 return { cn : [ cfg ] };
7429 initEvents : function()
7431 if(!this.store || !this.cm){
7434 if (this.selModel) {
7435 this.selModel.initEvents();
7439 //Roo.log('initEvents with ds!!!!');
7441 this.mainBody = this.el.select('tbody', true).first();
7442 this.mainHead = this.el.select('thead', true).first();
7443 this.mainFoot = this.el.select('tfoot', true).first();
7449 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7450 e.on('click', _this.sort, _this);
7453 this.mainBody.on("click", this.onClick, this);
7454 this.mainBody.on("dblclick", this.onDblClick, this);
7456 // why is this done????? = it breaks dialogs??
7457 //this.parent().el.setStyle('position', 'relative');
7461 this.footer.parentId = this.id;
7462 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7465 this.el.select('tfoot tr td').first().addClass('hide');
7470 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7473 this.store.on('load', this.onLoad, this);
7474 this.store.on('beforeload', this.onBeforeLoad, this);
7475 this.store.on('update', this.onUpdate, this);
7476 this.store.on('add', this.onAdd, this);
7477 this.store.on("clear", this.clear, this);
7479 this.el.on("contextmenu", this.onContextMenu, this);
7481 this.mainBody.on('scroll', this.onBodyScroll, this);
7483 this.cm.on("headerchange", this.onHeaderChange, this);
7485 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7489 onContextMenu : function(e, t)
7491 this.processEvent("contextmenu", e);
7494 processEvent : function(name, e)
7496 if (name != 'touchstart' ) {
7497 this.fireEvent(name, e);
7500 var t = e.getTarget();
7502 var cell = Roo.get(t);
7508 if(cell.findParent('tfoot', false, true)){
7512 if(cell.findParent('thead', false, true)){
7514 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7515 cell = Roo.get(t).findParent('th', false, true);
7517 Roo.log("failed to find th in thead?");
7518 Roo.log(e.getTarget());
7523 var cellIndex = cell.dom.cellIndex;
7525 var ename = name == 'touchstart' ? 'click' : name;
7526 this.fireEvent("header" + ename, this, cellIndex, e);
7531 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7532 cell = Roo.get(t).findParent('td', false, true);
7534 Roo.log("failed to find th in tbody?");
7535 Roo.log(e.getTarget());
7540 var row = cell.findParent('tr', false, true);
7541 var cellIndex = cell.dom.cellIndex;
7542 var rowIndex = row.dom.rowIndex - 1;
7546 this.fireEvent("row" + name, this, rowIndex, e);
7550 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7556 onMouseover : function(e, el)
7558 var cell = Roo.get(el);
7564 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7565 cell = cell.findParent('td', false, true);
7568 var row = cell.findParent('tr', false, true);
7569 var cellIndex = cell.dom.cellIndex;
7570 var rowIndex = row.dom.rowIndex - 1; // start from 0
7572 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7576 onMouseout : function(e, el)
7578 var cell = Roo.get(el);
7584 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7585 cell = cell.findParent('td', false, true);
7588 var row = cell.findParent('tr', false, true);
7589 var cellIndex = cell.dom.cellIndex;
7590 var rowIndex = row.dom.rowIndex - 1; // start from 0
7592 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7596 onClick : function(e, el)
7598 var cell = Roo.get(el);
7600 if(!cell || (!this.cellSelection && !this.rowSelection)){
7604 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7605 cell = cell.findParent('td', false, true);
7608 if(!cell || typeof(cell) == 'undefined'){
7612 var row = cell.findParent('tr', false, true);
7614 if(!row || typeof(row) == 'undefined'){
7618 var cellIndex = cell.dom.cellIndex;
7619 var rowIndex = this.getRowIndex(row);
7621 // why??? - should these not be based on SelectionModel?
7622 if(this.cellSelection){
7623 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7626 if(this.rowSelection){
7627 this.fireEvent('rowclick', this, row, rowIndex, e);
7633 onDblClick : function(e,el)
7635 var cell = Roo.get(el);
7637 if(!cell || (!this.cellSelection && !this.rowSelection)){
7641 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7642 cell = cell.findParent('td', false, true);
7645 if(!cell || typeof(cell) == 'undefined'){
7649 var row = cell.findParent('tr', false, true);
7651 if(!row || typeof(row) == 'undefined'){
7655 var cellIndex = cell.dom.cellIndex;
7656 var rowIndex = this.getRowIndex(row);
7658 if(this.cellSelection){
7659 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7662 if(this.rowSelection){
7663 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7667 sort : function(e,el)
7669 var col = Roo.get(el);
7671 if(!col.hasClass('sortable')){
7675 var sort = col.attr('sort');
7678 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7682 this.store.sortInfo = {field : sort, direction : dir};
7685 Roo.log("calling footer first");
7686 this.footer.onClick('first');
7689 this.store.load({ params : { start : 0 } });
7693 renderHeader : function()
7701 this.totalWidth = 0;
7703 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7705 var config = cm.config[i];
7709 cls : 'x-hcol-' + i,
7711 html: cm.getColumnHeader(i)
7716 if(typeof(config.sortable) != 'undefined' && config.sortable){
7718 c.html = '<i class="glyphicon"></i>' + c.html;
7721 // could use BS4 hidden-..-down
7723 if(typeof(config.lgHeader) != 'undefined'){
7724 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7727 if(typeof(config.mdHeader) != 'undefined'){
7728 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7731 if(typeof(config.smHeader) != 'undefined'){
7732 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7735 if(typeof(config.xsHeader) != 'undefined'){
7736 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7743 if(typeof(config.tooltip) != 'undefined'){
7744 c.tooltip = config.tooltip;
7747 if(typeof(config.colspan) != 'undefined'){
7748 c.colspan = config.colspan;
7751 if(typeof(config.hidden) != 'undefined' && config.hidden){
7752 c.style += ' display:none;';
7755 if(typeof(config.dataIndex) != 'undefined'){
7756 c.sort = config.dataIndex;
7761 if(typeof(config.align) != 'undefined' && config.align.length){
7762 c.style += ' text-align:' + config.align + ';';
7765 if(typeof(config.width) != 'undefined'){
7766 c.style += ' width:' + config.width + 'px;';
7767 this.totalWidth += config.width;
7769 this.totalWidth += 100; // assume minimum of 100 per column?
7772 if(typeof(config.cls) != 'undefined'){
7773 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7776 ['xs','sm','md','lg'].map(function(size){
7778 if(typeof(config[size]) == 'undefined'){
7782 if (!config[size]) { // 0 = hidden
7783 // BS 4 '0' is treated as hide that column and below.
7784 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7788 c.cls += ' col-' + size + '-' + config[size] + (
7789 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7801 renderBody : function()
7811 colspan : this.cm.getColumnCount()
7821 renderFooter : function()
7831 colspan : this.cm.getColumnCount()
7845 // Roo.log('ds onload');
7850 var ds = this.store;
7852 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7853 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7854 if (_this.store.sortInfo) {
7856 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7857 e.select('i', true).addClass(['glyphicon-arrow-up']);
7860 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7861 e.select('i', true).addClass(['glyphicon-arrow-down']);
7866 var tbody = this.mainBody;
7868 if(ds.getCount() > 0){
7869 ds.data.each(function(d,rowIndex){
7870 var row = this.renderRow(cm, ds, rowIndex);
7872 tbody.createChild(row);
7876 if(row.cellObjects.length){
7877 Roo.each(row.cellObjects, function(r){
7878 _this.renderCellObject(r);
7885 var tfoot = this.el.select('tfoot', true).first();
7887 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7889 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7891 var total = this.ds.getTotalCount();
7893 if(this.footer.pageSize < total){
7894 this.mainFoot.show();
7898 Roo.each(this.el.select('tbody td', true).elements, function(e){
7899 e.on('mouseover', _this.onMouseover, _this);
7902 Roo.each(this.el.select('tbody td', true).elements, function(e){
7903 e.on('mouseout', _this.onMouseout, _this);
7905 this.fireEvent('rowsrendered', this);
7911 onUpdate : function(ds,record)
7913 this.refreshRow(record);
7917 onRemove : function(ds, record, index, isUpdate){
7918 if(isUpdate !== true){
7919 this.fireEvent("beforerowremoved", this, index, record);
7921 var bt = this.mainBody.dom;
7923 var rows = this.el.select('tbody > tr', true).elements;
7925 if(typeof(rows[index]) != 'undefined'){
7926 bt.removeChild(rows[index].dom);
7929 // if(bt.rows[index]){
7930 // bt.removeChild(bt.rows[index]);
7933 if(isUpdate !== true){
7934 //this.stripeRows(index);
7935 //this.syncRowHeights(index, index);
7937 this.fireEvent("rowremoved", this, index, record);
7941 onAdd : function(ds, records, rowIndex)
7943 //Roo.log('on Add called');
7944 // - note this does not handle multiple adding very well..
7945 var bt = this.mainBody.dom;
7946 for (var i =0 ; i < records.length;i++) {
7947 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7948 //Roo.log(records[i]);
7949 //Roo.log(this.store.getAt(rowIndex+i));
7950 this.insertRow(this.store, rowIndex + i, false);
7957 refreshRow : function(record){
7958 var ds = this.store, index;
7959 if(typeof record == 'number'){
7961 record = ds.getAt(index);
7963 index = ds.indexOf(record);
7965 this.insertRow(ds, index, true);
7967 this.onRemove(ds, record, index+1, true);
7969 //this.syncRowHeights(index, index);
7971 this.fireEvent("rowupdated", this, index, record);
7974 insertRow : function(dm, rowIndex, isUpdate){
7977 this.fireEvent("beforerowsinserted", this, rowIndex);
7979 //var s = this.getScrollState();
7980 var row = this.renderRow(this.cm, this.store, rowIndex);
7981 // insert before rowIndex..
7982 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7986 if(row.cellObjects.length){
7987 Roo.each(row.cellObjects, function(r){
7988 _this.renderCellObject(r);
7993 this.fireEvent("rowsinserted", this, rowIndex);
7994 //this.syncRowHeights(firstRow, lastRow);
7995 //this.stripeRows(firstRow);
8002 getRowDom : function(rowIndex)
8004 var rows = this.el.select('tbody > tr', true).elements;
8006 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8009 // returns the object tree for a tr..
8012 renderRow : function(cm, ds, rowIndex)
8014 var d = ds.getAt(rowIndex);
8018 cls : 'x-row-' + rowIndex,
8022 var cellObjects = [];
8024 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8025 var config = cm.config[i];
8027 var renderer = cm.getRenderer(i);
8031 if(typeof(renderer) !== 'undefined'){
8032 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8034 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8035 // and are rendered into the cells after the row is rendered - using the id for the element.
8037 if(typeof(value) === 'object'){
8047 rowIndex : rowIndex,
8052 this.fireEvent('rowclass', this, rowcfg);
8056 cls : rowcfg.rowClass + ' x-col-' + i,
8058 html: (typeof(value) === 'object') ? '' : value
8065 if(typeof(config.colspan) != 'undefined'){
8066 td.colspan = config.colspan;
8069 if(typeof(config.hidden) != 'undefined' && config.hidden){
8070 td.style += ' display:none;';
8073 if(typeof(config.align) != 'undefined' && config.align.length){
8074 td.style += ' text-align:' + config.align + ';';
8076 if(typeof(config.valign) != 'undefined' && config.valign.length){
8077 td.style += ' vertical-align:' + config.valign + ';';
8080 if(typeof(config.width) != 'undefined'){
8081 td.style += ' width:' + config.width + 'px;';
8084 if(typeof(config.cursor) != 'undefined'){
8085 td.style += ' cursor:' + config.cursor + ';';
8088 if(typeof(config.cls) != 'undefined'){
8089 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8092 ['xs','sm','md','lg'].map(function(size){
8094 if(typeof(config[size]) == 'undefined'){
8100 if (!config[size]) { // 0 = hidden
8101 // BS 4 '0' is treated as hide that column and below.
8102 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8106 td.cls += ' col-' + size + '-' + config[size] + (
8107 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8117 row.cellObjects = cellObjects;
8125 onBeforeLoad : function()
8134 this.el.select('tbody', true).first().dom.innerHTML = '';
8137 * Show or hide a row.
8138 * @param {Number} rowIndex to show or hide
8139 * @param {Boolean} state hide
8141 setRowVisibility : function(rowIndex, state)
8143 var bt = this.mainBody.dom;
8145 var rows = this.el.select('tbody > tr', true).elements;
8147 if(typeof(rows[rowIndex]) == 'undefined'){
8150 rows[rowIndex].dom.style.display = state ? '' : 'none';
8154 getSelectionModel : function(){
8156 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8158 return this.selModel;
8161 * Render the Roo.bootstrap object from renderder
8163 renderCellObject : function(r)
8167 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8169 var t = r.cfg.render(r.container);
8172 Roo.each(r.cfg.cn, function(c){
8174 container: t.getChildContainer(),
8177 _this.renderCellObject(child);
8182 getRowIndex : function(row)
8186 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8197 * Returns the grid's underlying element = used by panel.Grid
8198 * @return {Element} The element
8200 getGridEl : function(){
8204 * Forces a resize - used by panel.Grid
8205 * @return {Element} The element
8207 autoSize : function()
8209 //var ctr = Roo.get(this.container.dom.parentElement);
8210 var ctr = Roo.get(this.el.dom);
8212 var thd = this.getGridEl().select('thead',true).first();
8213 var tbd = this.getGridEl().select('tbody', true).first();
8214 var tfd = this.getGridEl().select('tfoot', true).first();
8216 var cw = ctr.getWidth();
8220 tbd.setWidth(ctr.getWidth());
8221 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8222 // this needs fixing for various usage - currently only hydra job advers I think..
8224 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8226 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8229 cw = Math.max(cw, this.totalWidth);
8230 this.getGridEl().select('tr',true).setWidth(cw);
8231 // resize 'expandable coloumn?
8233 return; // we doe not have a view in this design..
8236 onBodyScroll: function()
8238 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8240 this.mainHead.setStyle({
8241 'position' : 'relative',
8242 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8248 var scrollHeight = this.mainBody.dom.scrollHeight;
8250 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8252 var height = this.mainBody.getHeight();
8254 if(scrollHeight - height == scrollTop) {
8256 var total = this.ds.getTotalCount();
8258 if(this.footer.cursor + this.footer.pageSize < total){
8260 this.footer.ds.load({
8262 start : this.footer.cursor + this.footer.pageSize,
8263 limit : this.footer.pageSize
8273 onHeaderChange : function()
8275 var header = this.renderHeader();
8276 var table = this.el.select('table', true).first();
8278 this.mainHead.remove();
8279 this.mainHead = table.createChild(header, this.mainBody, false);
8282 onHiddenChange : function(colModel, colIndex, hidden)
8284 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8285 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8287 this.CSS.updateRule(thSelector, "display", "");
8288 this.CSS.updateRule(tdSelector, "display", "");
8291 this.CSS.updateRule(thSelector, "display", "none");
8292 this.CSS.updateRule(tdSelector, "display", "none");
8295 this.onHeaderChange();
8299 setColumnWidth: function(col_index, width)
8301 // width = "md-2 xs-2..."
8302 if(!this.colModel.config[col_index]) {
8306 var w = width.split(" ");
8308 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8310 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8313 for(var j = 0; j < w.length; j++) {
8319 var size_cls = w[j].split("-");
8321 if(!Number.isInteger(size_cls[1] * 1)) {
8325 if(!this.colModel.config[col_index][size_cls[0]]) {
8329 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8333 h_row[0].classList.replace(
8334 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8335 "col-"+size_cls[0]+"-"+size_cls[1]
8338 for(var i = 0; i < rows.length; i++) {
8340 var size_cls = w[j].split("-");
8342 if(!Number.isInteger(size_cls[1] * 1)) {
8346 if(!this.colModel.config[col_index][size_cls[0]]) {
8350 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8354 rows[i].classList.replace(
8355 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8356 "col-"+size_cls[0]+"-"+size_cls[1]
8360 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8375 * @class Roo.bootstrap.TableCell
8376 * @extends Roo.bootstrap.Component
8377 * Bootstrap TableCell class
8378 * @cfg {String} html cell contain text
8379 * @cfg {String} cls cell class
8380 * @cfg {String} tag cell tag (td|th) default td
8381 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8382 * @cfg {String} align Aligns the content in a cell
8383 * @cfg {String} axis Categorizes cells
8384 * @cfg {String} bgcolor Specifies the background color of a cell
8385 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8386 * @cfg {Number} colspan Specifies the number of columns a cell should span
8387 * @cfg {String} headers Specifies one or more header cells a cell is related to
8388 * @cfg {Number} height Sets the height of a cell
8389 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8390 * @cfg {Number} rowspan Sets the number of rows a cell should span
8391 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8392 * @cfg {String} valign Vertical aligns the content in a cell
8393 * @cfg {Number} width Specifies the width of a cell
8396 * Create a new TableCell
8397 * @param {Object} config The config object
8400 Roo.bootstrap.TableCell = function(config){
8401 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8404 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8424 getAutoCreate : function(){
8425 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8445 cfg.align=this.align
8451 cfg.bgcolor=this.bgcolor
8454 cfg.charoff=this.charoff
8457 cfg.colspan=this.colspan
8460 cfg.headers=this.headers
8463 cfg.height=this.height
8466 cfg.nowrap=this.nowrap
8469 cfg.rowspan=this.rowspan
8472 cfg.scope=this.scope
8475 cfg.valign=this.valign
8478 cfg.width=this.width
8497 * @class Roo.bootstrap.TableRow
8498 * @extends Roo.bootstrap.Component
8499 * Bootstrap TableRow class
8500 * @cfg {String} cls row class
8501 * @cfg {String} align Aligns the content in a table row
8502 * @cfg {String} bgcolor Specifies a background color for a table row
8503 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8504 * @cfg {String} valign Vertical aligns the content in a table row
8507 * Create a new TableRow
8508 * @param {Object} config The config object
8511 Roo.bootstrap.TableRow = function(config){
8512 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8515 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8523 getAutoCreate : function(){
8524 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8534 cfg.align = this.align;
8537 cfg.bgcolor = this.bgcolor;
8540 cfg.charoff = this.charoff;
8543 cfg.valign = this.valign;
8561 * @class Roo.bootstrap.TableBody
8562 * @extends Roo.bootstrap.Component
8563 * Bootstrap TableBody class
8564 * @cfg {String} cls element class
8565 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8566 * @cfg {String} align Aligns the content inside the element
8567 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8568 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8571 * Create a new TableBody
8572 * @param {Object} config The config object
8575 Roo.bootstrap.TableBody = function(config){
8576 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8579 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8587 getAutoCreate : function(){
8588 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8602 cfg.align = this.align;
8605 cfg.charoff = this.charoff;
8608 cfg.valign = this.valign;
8615 // initEvents : function()
8622 // this.store = Roo.factory(this.store, Roo.data);
8623 // this.store.on('load', this.onLoad, this);
8625 // this.store.load();
8629 // onLoad: function ()
8631 // this.fireEvent('load', this);
8641 * Ext JS Library 1.1.1
8642 * Copyright(c) 2006-2007, Ext JS, LLC.
8644 * Originally Released Under LGPL - original licence link has changed is not relivant.
8647 * <script type="text/javascript">
8650 // as we use this in bootstrap.
8651 Roo.namespace('Roo.form');
8653 * @class Roo.form.Action
8654 * Internal Class used to handle form actions
8656 * @param {Roo.form.BasicForm} el The form element or its id
8657 * @param {Object} config Configuration options
8662 // define the action interface
8663 Roo.form.Action = function(form, options){
8665 this.options = options || {};
8668 * Client Validation Failed
8671 Roo.form.Action.CLIENT_INVALID = 'client';
8673 * Server Validation Failed
8676 Roo.form.Action.SERVER_INVALID = 'server';
8678 * Connect to Server Failed
8681 Roo.form.Action.CONNECT_FAILURE = 'connect';
8683 * Reading Data from Server Failed
8686 Roo.form.Action.LOAD_FAILURE = 'load';
8688 Roo.form.Action.prototype = {
8690 failureType : undefined,
8691 response : undefined,
8695 run : function(options){
8700 success : function(response){
8705 handleResponse : function(response){
8709 // default connection failure
8710 failure : function(response){
8712 this.response = response;
8713 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8714 this.form.afterAction(this, false);
8717 processResponse : function(response){
8718 this.response = response;
8719 if(!response.responseText){
8722 this.result = this.handleResponse(response);
8726 // utility functions used internally
8727 getUrl : function(appendParams){
8728 var url = this.options.url || this.form.url || this.form.el.dom.action;
8730 var p = this.getParams();
8732 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8738 getMethod : function(){
8739 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8742 getParams : function(){
8743 var bp = this.form.baseParams;
8744 var p = this.options.params;
8746 if(typeof p == "object"){
8747 p = Roo.urlEncode(Roo.applyIf(p, bp));
8748 }else if(typeof p == 'string' && bp){
8749 p += '&' + Roo.urlEncode(bp);
8752 p = Roo.urlEncode(bp);
8757 createCallback : function(){
8759 success: this.success,
8760 failure: this.failure,
8762 timeout: (this.form.timeout*1000),
8763 upload: this.form.fileUpload ? this.success : undefined
8768 Roo.form.Action.Submit = function(form, options){
8769 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8772 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8775 haveProgress : false,
8776 uploadComplete : false,
8778 // uploadProgress indicator.
8779 uploadProgress : function()
8781 if (!this.form.progressUrl) {
8785 if (!this.haveProgress) {
8786 Roo.MessageBox.progress("Uploading", "Uploading");
8788 if (this.uploadComplete) {
8789 Roo.MessageBox.hide();
8793 this.haveProgress = true;
8795 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8797 var c = new Roo.data.Connection();
8799 url : this.form.progressUrl,
8804 success : function(req){
8805 //console.log(data);
8809 rdata = Roo.decode(req.responseText)
8811 Roo.log("Invalid data from server..");
8815 if (!rdata || !rdata.success) {
8817 Roo.MessageBox.alert(Roo.encode(rdata));
8820 var data = rdata.data;
8822 if (this.uploadComplete) {
8823 Roo.MessageBox.hide();
8828 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8829 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8832 this.uploadProgress.defer(2000,this);
8835 failure: function(data) {
8836 Roo.log('progress url failed ');
8847 // run get Values on the form, so it syncs any secondary forms.
8848 this.form.getValues();
8850 var o = this.options;
8851 var method = this.getMethod();
8852 var isPost = method == 'POST';
8853 if(o.clientValidation === false || this.form.isValid()){
8855 if (this.form.progressUrl) {
8856 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8857 (new Date() * 1) + '' + Math.random());
8862 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8863 form:this.form.el.dom,
8864 url:this.getUrl(!isPost),
8866 params:isPost ? this.getParams() : null,
8867 isUpload: this.form.fileUpload,
8868 formData : this.form.formData
8871 this.uploadProgress();
8873 }else if (o.clientValidation !== false){ // client validation failed
8874 this.failureType = Roo.form.Action.CLIENT_INVALID;
8875 this.form.afterAction(this, false);
8879 success : function(response)
8881 this.uploadComplete= true;
8882 if (this.haveProgress) {
8883 Roo.MessageBox.hide();
8887 var result = this.processResponse(response);
8888 if(result === true || result.success){
8889 this.form.afterAction(this, true);
8893 this.form.markInvalid(result.errors);
8894 this.failureType = Roo.form.Action.SERVER_INVALID;
8896 this.form.afterAction(this, false);
8898 failure : function(response)
8900 this.uploadComplete= true;
8901 if (this.haveProgress) {
8902 Roo.MessageBox.hide();
8905 this.response = response;
8906 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8907 this.form.afterAction(this, false);
8910 handleResponse : function(response){
8911 if(this.form.errorReader){
8912 var rs = this.form.errorReader.read(response);
8915 for(var i = 0, len = rs.records.length; i < len; i++) {
8916 var r = rs.records[i];
8920 if(errors.length < 1){
8924 success : rs.success,
8930 ret = Roo.decode(response.responseText);
8934 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8944 Roo.form.Action.Load = function(form, options){
8945 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8946 this.reader = this.form.reader;
8949 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8954 Roo.Ajax.request(Roo.apply(
8955 this.createCallback(), {
8956 method:this.getMethod(),
8957 url:this.getUrl(false),
8958 params:this.getParams()
8962 success : function(response){
8964 var result = this.processResponse(response);
8965 if(result === true || !result.success || !result.data){
8966 this.failureType = Roo.form.Action.LOAD_FAILURE;
8967 this.form.afterAction(this, false);
8970 this.form.clearInvalid();
8971 this.form.setValues(result.data);
8972 this.form.afterAction(this, true);
8975 handleResponse : function(response){
8976 if(this.form.reader){
8977 var rs = this.form.reader.read(response);
8978 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8980 success : rs.success,
8984 return Roo.decode(response.responseText);
8988 Roo.form.Action.ACTION_TYPES = {
8989 'load' : Roo.form.Action.Load,
8990 'submit' : Roo.form.Action.Submit
8999 * @class Roo.bootstrap.Form
9000 * @extends Roo.bootstrap.Component
9001 * Bootstrap Form class
9002 * @cfg {String} method GET | POST (default POST)
9003 * @cfg {String} labelAlign top | left (default top)
9004 * @cfg {String} align left | right - for navbars
9005 * @cfg {Boolean} loadMask load mask when submit (default true)
9010 * @param {Object} config The config object
9014 Roo.bootstrap.Form = function(config){
9016 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9018 Roo.bootstrap.Form.popover.apply();
9022 * @event clientvalidation
9023 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9024 * @param {Form} this
9025 * @param {Boolean} valid true if the form has passed client-side validation
9027 clientvalidation: true,
9029 * @event beforeaction
9030 * Fires before any action is performed. Return false to cancel the action.
9031 * @param {Form} this
9032 * @param {Action} action The action to be performed
9036 * @event actionfailed
9037 * Fires when an action fails.
9038 * @param {Form} this
9039 * @param {Action} action The action that failed
9041 actionfailed : true,
9043 * @event actioncomplete
9044 * Fires when an action is completed.
9045 * @param {Form} this
9046 * @param {Action} action The action that completed
9048 actioncomplete : true
9052 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9055 * @cfg {String} method
9056 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9061 * The URL to use for form actions if one isn't supplied in the action options.
9064 * @cfg {Boolean} fileUpload
9065 * Set to true if this form is a file upload.
9069 * @cfg {Object} baseParams
9070 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9074 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9078 * @cfg {Sting} align (left|right) for navbar forms
9083 activeAction : null,
9086 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9087 * element by passing it or its id or mask the form itself by passing in true.
9090 waitMsgTarget : false,
9095 * @cfg {Boolean} errorMask (true|false) default false
9100 * @cfg {Number} maskOffset Default 100
9105 * @cfg {Boolean} maskBody
9109 getAutoCreate : function(){
9113 method : this.method || 'POST',
9114 id : this.id || Roo.id(),
9117 if (this.parent().xtype.match(/^Nav/)) {
9118 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9122 if (this.labelAlign == 'left' ) {
9123 cfg.cls += ' form-horizontal';
9129 initEvents : function()
9131 this.el.on('submit', this.onSubmit, this);
9132 // this was added as random key presses on the form where triggering form submit.
9133 this.el.on('keypress', function(e) {
9134 if (e.getCharCode() != 13) {
9137 // we might need to allow it for textareas.. and some other items.
9138 // check e.getTarget().
9140 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9144 Roo.log("keypress blocked");
9152 onSubmit : function(e){
9157 * Returns true if client-side validation on the form is successful.
9160 isValid : function(){
9161 var items = this.getItems();
9165 items.each(function(f){
9171 Roo.log('invalid field: ' + f.name);
9175 if(!target && f.el.isVisible(true)){
9181 if(this.errorMask && !valid){
9182 Roo.bootstrap.Form.popover.mask(this, target);
9189 * Returns true if any fields in this form have changed since their original load.
9192 isDirty : function(){
9194 var items = this.getItems();
9195 items.each(function(f){
9205 * Performs a predefined action (submit or load) or custom actions you define on this form.
9206 * @param {String} actionName The name of the action type
9207 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9208 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9209 * accept other config options):
9211 Property Type Description
9212 ---------------- --------------- ----------------------------------------------------------------------------------
9213 url String The url for the action (defaults to the form's url)
9214 method String The form method to use (defaults to the form's method, or POST if not defined)
9215 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9216 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9217 validate the form on the client (defaults to false)
9219 * @return {BasicForm} this
9221 doAction : function(action, options){
9222 if(typeof action == 'string'){
9223 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9225 if(this.fireEvent('beforeaction', this, action) !== false){
9226 this.beforeAction(action);
9227 action.run.defer(100, action);
9233 beforeAction : function(action){
9234 var o = action.options;
9239 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9241 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9244 // not really supported yet.. ??
9246 //if(this.waitMsgTarget === true){
9247 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9248 //}else if(this.waitMsgTarget){
9249 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9250 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9252 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9258 afterAction : function(action, success){
9259 this.activeAction = null;
9260 var o = action.options;
9265 Roo.get(document.body).unmask();
9271 //if(this.waitMsgTarget === true){
9272 // this.el.unmask();
9273 //}else if(this.waitMsgTarget){
9274 // this.waitMsgTarget.unmask();
9276 // Roo.MessageBox.updateProgress(1);
9277 // Roo.MessageBox.hide();
9284 Roo.callback(o.success, o.scope, [this, action]);
9285 this.fireEvent('actioncomplete', this, action);
9289 // failure condition..
9290 // we have a scenario where updates need confirming.
9291 // eg. if a locking scenario exists..
9292 // we look for { errors : { needs_confirm : true }} in the response.
9294 (typeof(action.result) != 'undefined') &&
9295 (typeof(action.result.errors) != 'undefined') &&
9296 (typeof(action.result.errors.needs_confirm) != 'undefined')
9299 Roo.log("not supported yet");
9302 Roo.MessageBox.confirm(
9303 "Change requires confirmation",
9304 action.result.errorMsg,
9309 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9319 Roo.callback(o.failure, o.scope, [this, action]);
9320 // show an error message if no failed handler is set..
9321 if (!this.hasListener('actionfailed')) {
9322 Roo.log("need to add dialog support");
9324 Roo.MessageBox.alert("Error",
9325 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9326 action.result.errorMsg :
9327 "Saving Failed, please check your entries or try again"
9332 this.fireEvent('actionfailed', this, action);
9337 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9338 * @param {String} id The value to search for
9341 findField : function(id){
9342 var items = this.getItems();
9343 var field = items.get(id);
9345 items.each(function(f){
9346 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9353 return field || null;
9356 * Mark fields in this form invalid in bulk.
9357 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9358 * @return {BasicForm} this
9360 markInvalid : function(errors){
9361 if(errors instanceof Array){
9362 for(var i = 0, len = errors.length; i < len; i++){
9363 var fieldError = errors[i];
9364 var f = this.findField(fieldError.id);
9366 f.markInvalid(fieldError.msg);
9372 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9373 field.markInvalid(errors[id]);
9377 //Roo.each(this.childForms || [], function (f) {
9378 // f.markInvalid(errors);
9385 * Set values for fields in this form in bulk.
9386 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9387 * @return {BasicForm} this
9389 setValues : function(values){
9390 if(values instanceof Array){ // array of objects
9391 for(var i = 0, len = values.length; i < len; i++){
9393 var f = this.findField(v.id);
9395 f.setValue(v.value);
9396 if(this.trackResetOnLoad){
9397 f.originalValue = f.getValue();
9401 }else{ // object hash
9404 if(typeof values[id] != 'function' && (field = this.findField(id))){
9406 if (field.setFromData &&
9408 field.displayField &&
9409 // combos' with local stores can
9410 // be queried via setValue()
9411 // to set their value..
9412 (field.store && !field.store.isLocal)
9416 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9417 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9418 field.setFromData(sd);
9420 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9422 field.setFromData(values);
9425 field.setValue(values[id]);
9429 if(this.trackResetOnLoad){
9430 field.originalValue = field.getValue();
9436 //Roo.each(this.childForms || [], function (f) {
9437 // f.setValues(values);
9444 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9445 * they are returned as an array.
9446 * @param {Boolean} asString
9449 getValues : function(asString){
9450 //if (this.childForms) {
9451 // copy values from the child forms
9452 // Roo.each(this.childForms, function (f) {
9453 // this.setValues(f.getValues());
9459 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9460 if(asString === true){
9463 return Roo.urlDecode(fs);
9467 * Returns the fields in this form as an object with key/value pairs.
9468 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9471 getFieldValues : function(with_hidden)
9473 var items = this.getItems();
9475 items.each(function(f){
9481 var v = f.getValue();
9483 if (f.inputType =='radio') {
9484 if (typeof(ret[f.getName()]) == 'undefined') {
9485 ret[f.getName()] = ''; // empty..
9488 if (!f.el.dom.checked) {
9496 if(f.xtype == 'MoneyField'){
9497 ret[f.currencyName] = f.getCurrency();
9500 // not sure if this supported any more..
9501 if ((typeof(v) == 'object') && f.getRawValue) {
9502 v = f.getRawValue() ; // dates..
9504 // combo boxes where name != hiddenName...
9505 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9506 ret[f.name] = f.getRawValue();
9508 ret[f.getName()] = v;
9515 * Clears all invalid messages in this form.
9516 * @return {BasicForm} this
9518 clearInvalid : function(){
9519 var items = this.getItems();
9521 items.each(function(f){
9530 * @return {BasicForm} this
9533 var items = this.getItems();
9534 items.each(function(f){
9538 Roo.each(this.childForms || [], function (f) {
9546 getItems : function()
9548 var r=new Roo.util.MixedCollection(false, function(o){
9549 return o.id || (o.id = Roo.id());
9551 var iter = function(el) {
9558 Roo.each(el.items,function(e) {
9567 hideFields : function(items)
9569 Roo.each(items, function(i){
9571 var f = this.findField(i);
9582 showFields : function(items)
9584 Roo.each(items, function(i){
9586 var f = this.findField(i);
9599 Roo.apply(Roo.bootstrap.Form, {
9626 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9627 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9628 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9629 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9632 this.maskEl.top.enableDisplayMode("block");
9633 this.maskEl.left.enableDisplayMode("block");
9634 this.maskEl.bottom.enableDisplayMode("block");
9635 this.maskEl.right.enableDisplayMode("block");
9637 this.toolTip = new Roo.bootstrap.Tooltip({
9638 cls : 'roo-form-error-popover',
9640 'left' : ['r-l', [-2,0], 'right'],
9641 'right' : ['l-r', [2,0], 'left'],
9642 'bottom' : ['tl-bl', [0,2], 'top'],
9643 'top' : [ 'bl-tl', [0,-2], 'bottom']
9647 this.toolTip.render(Roo.get(document.body));
9649 this.toolTip.el.enableDisplayMode("block");
9651 Roo.get(document.body).on('click', function(){
9655 Roo.get(document.body).on('touchstart', function(){
9659 this.isApplied = true
9662 mask : function(form, target)
9666 this.target = target;
9668 if(!this.form.errorMask || !target.el){
9672 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9674 Roo.log(scrollable);
9676 var ot = this.target.el.calcOffsetsTo(scrollable);
9678 var scrollTo = ot[1] - this.form.maskOffset;
9680 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9682 scrollable.scrollTo('top', scrollTo);
9684 var box = this.target.el.getBox();
9686 var zIndex = Roo.bootstrap.Modal.zIndex++;
9689 this.maskEl.top.setStyle('position', 'absolute');
9690 this.maskEl.top.setStyle('z-index', zIndex);
9691 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9692 this.maskEl.top.setLeft(0);
9693 this.maskEl.top.setTop(0);
9694 this.maskEl.top.show();
9696 this.maskEl.left.setStyle('position', 'absolute');
9697 this.maskEl.left.setStyle('z-index', zIndex);
9698 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9699 this.maskEl.left.setLeft(0);
9700 this.maskEl.left.setTop(box.y - this.padding);
9701 this.maskEl.left.show();
9703 this.maskEl.bottom.setStyle('position', 'absolute');
9704 this.maskEl.bottom.setStyle('z-index', zIndex);
9705 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9706 this.maskEl.bottom.setLeft(0);
9707 this.maskEl.bottom.setTop(box.bottom + this.padding);
9708 this.maskEl.bottom.show();
9710 this.maskEl.right.setStyle('position', 'absolute');
9711 this.maskEl.right.setStyle('z-index', zIndex);
9712 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9713 this.maskEl.right.setLeft(box.right + this.padding);
9714 this.maskEl.right.setTop(box.y - this.padding);
9715 this.maskEl.right.show();
9717 this.toolTip.bindEl = this.target.el;
9719 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9721 var tip = this.target.blankText;
9723 if(this.target.getValue() !== '' ) {
9725 if (this.target.invalidText.length) {
9726 tip = this.target.invalidText;
9727 } else if (this.target.regexText.length){
9728 tip = this.target.regexText;
9732 this.toolTip.show(tip);
9734 this.intervalID = window.setInterval(function() {
9735 Roo.bootstrap.Form.popover.unmask();
9738 window.onwheel = function(){ return false;};
9740 (function(){ this.isMasked = true; }).defer(500, this);
9746 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9750 this.maskEl.top.setStyle('position', 'absolute');
9751 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9752 this.maskEl.top.hide();
9754 this.maskEl.left.setStyle('position', 'absolute');
9755 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9756 this.maskEl.left.hide();
9758 this.maskEl.bottom.setStyle('position', 'absolute');
9759 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9760 this.maskEl.bottom.hide();
9762 this.maskEl.right.setStyle('position', 'absolute');
9763 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9764 this.maskEl.right.hide();
9766 this.toolTip.hide();
9768 this.toolTip.el.hide();
9770 window.onwheel = function(){ return true;};
9772 if(this.intervalID){
9773 window.clearInterval(this.intervalID);
9774 this.intervalID = false;
9777 this.isMasked = false;
9787 * Ext JS Library 1.1.1
9788 * Copyright(c) 2006-2007, Ext JS, LLC.
9790 * Originally Released Under LGPL - original licence link has changed is not relivant.
9793 * <script type="text/javascript">
9796 * @class Roo.form.VTypes
9797 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9800 Roo.form.VTypes = function(){
9801 // closure these in so they are only created once.
9802 var alpha = /^[a-zA-Z_]+$/;
9803 var alphanum = /^[a-zA-Z0-9_]+$/;
9804 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9805 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9807 // All these messages and functions are configurable
9810 * The function used to validate email addresses
9811 * @param {String} value The email address
9813 'email' : function(v){
9814 return email.test(v);
9817 * The error text to display when the email validation function returns false
9820 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9822 * The keystroke filter mask to be applied on email input
9825 'emailMask' : /[a-z0-9_\.\-@]/i,
9828 * The function used to validate URLs
9829 * @param {String} value The URL
9831 'url' : function(v){
9835 * The error text to display when the url validation function returns false
9838 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9841 * The function used to validate alpha values
9842 * @param {String} value The value
9844 'alpha' : function(v){
9845 return alpha.test(v);
9848 * The error text to display when the alpha validation function returns false
9851 'alphaText' : 'This field should only contain letters and _',
9853 * The keystroke filter mask to be applied on alpha input
9856 'alphaMask' : /[a-z_]/i,
9859 * The function used to validate alphanumeric values
9860 * @param {String} value The value
9862 'alphanum' : function(v){
9863 return alphanum.test(v);
9866 * The error text to display when the alphanumeric validation function returns false
9869 'alphanumText' : 'This field should only contain letters, numbers and _',
9871 * The keystroke filter mask to be applied on alphanumeric input
9874 'alphanumMask' : /[a-z0-9_]/i
9884 * @class Roo.bootstrap.Input
9885 * @extends Roo.bootstrap.Component
9886 * Bootstrap Input class
9887 * @cfg {Boolean} disabled is it disabled
9888 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9889 * @cfg {String} name name of the input
9890 * @cfg {string} fieldLabel - the label associated
9891 * @cfg {string} placeholder - placeholder to put in text.
9892 * @cfg {string} before - input group add on before
9893 * @cfg {string} after - input group add on after
9894 * @cfg {string} size - (lg|sm) or leave empty..
9895 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9896 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9897 * @cfg {Number} md colspan out of 12 for computer-sized screens
9898 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9899 * @cfg {string} value default value of the input
9900 * @cfg {Number} labelWidth set the width of label
9901 * @cfg {Number} labellg set the width of label (1-12)
9902 * @cfg {Number} labelmd set the width of label (1-12)
9903 * @cfg {Number} labelsm set the width of label (1-12)
9904 * @cfg {Number} labelxs set the width of label (1-12)
9905 * @cfg {String} labelAlign (top|left)
9906 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9907 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9908 * @cfg {String} indicatorpos (left|right) default left
9909 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9910 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9912 * @cfg {String} align (left|center|right) Default left
9913 * @cfg {Boolean} forceFeedback (true|false) Default false
9916 * Create a new Input
9917 * @param {Object} config The config object
9920 Roo.bootstrap.Input = function(config){
9922 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9927 * Fires when this field receives input focus.
9928 * @param {Roo.form.Field} this
9933 * Fires when this field loses input focus.
9934 * @param {Roo.form.Field} this
9939 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9940 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9941 * @param {Roo.form.Field} this
9942 * @param {Roo.EventObject} e The event object
9947 * Fires just before the field blurs if the field value has changed.
9948 * @param {Roo.form.Field} this
9949 * @param {Mixed} newValue The new value
9950 * @param {Mixed} oldValue The original value
9955 * Fires after the field has been marked as invalid.
9956 * @param {Roo.form.Field} this
9957 * @param {String} msg The validation message
9962 * Fires after the field has been validated with no errors.
9963 * @param {Roo.form.Field} this
9968 * Fires after the key up
9969 * @param {Roo.form.Field} this
9970 * @param {Roo.EventObject} e The event Object
9976 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9978 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9979 automatic validation (defaults to "keyup").
9981 validationEvent : "keyup",
9983 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9985 validateOnBlur : true,
9987 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9989 validationDelay : 250,
9991 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9993 focusClass : "x-form-focus", // not needed???
9997 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9999 invalidClass : "has-warning",
10002 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10004 validClass : "has-success",
10007 * @cfg {Boolean} hasFeedback (true|false) default true
10009 hasFeedback : true,
10012 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10014 invalidFeedbackClass : "glyphicon-warning-sign",
10017 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10019 validFeedbackClass : "glyphicon-ok",
10022 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10024 selectOnFocus : false,
10027 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10031 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10036 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10038 disableKeyFilter : false,
10041 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10045 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10049 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10051 blankText : "Please complete this mandatory field",
10054 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10058 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10060 maxLength : Number.MAX_VALUE,
10062 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10064 minLengthText : "The minimum length for this field is {0}",
10066 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10068 maxLengthText : "The maximum length for this field is {0}",
10072 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10073 * If available, this function will be called only after the basic validators all return true, and will be passed the
10074 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10078 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10079 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10080 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10084 * @cfg {String} regexText -- Depricated - use Invalid Text
10089 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10095 autocomplete: false,
10099 inputType : 'text',
10102 placeholder: false,
10107 preventMark: false,
10108 isFormField : true,
10111 labelAlign : false,
10114 formatedValue : false,
10115 forceFeedback : false,
10117 indicatorpos : 'left',
10127 parentLabelAlign : function()
10130 while (parent.parent()) {
10131 parent = parent.parent();
10132 if (typeof(parent.labelAlign) !='undefined') {
10133 return parent.labelAlign;
10140 getAutoCreate : function()
10142 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10148 if(this.inputType != 'hidden'){
10149 cfg.cls = 'form-group' //input-group
10155 type : this.inputType,
10156 value : this.value,
10157 cls : 'form-control',
10158 placeholder : this.placeholder || '',
10159 autocomplete : this.autocomplete || 'new-password'
10162 if(this.capture.length){
10163 input.capture = this.capture;
10166 if(this.accept.length){
10167 input.accept = this.accept + "/*";
10171 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10174 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10175 input.maxLength = this.maxLength;
10178 if (this.disabled) {
10179 input.disabled=true;
10182 if (this.readOnly) {
10183 input.readonly=true;
10187 input.name = this.name;
10191 input.cls += ' input-' + this.size;
10195 ['xs','sm','md','lg'].map(function(size){
10196 if (settings[size]) {
10197 cfg.cls += ' col-' + size + '-' + settings[size];
10201 var inputblock = input;
10205 cls: 'glyphicon form-control-feedback'
10208 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10211 cls : 'has-feedback',
10219 if (this.before || this.after) {
10222 cls : 'input-group',
10226 if (this.before && typeof(this.before) == 'string') {
10228 inputblock.cn.push({
10230 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10234 if (this.before && typeof(this.before) == 'object') {
10235 this.before = Roo.factory(this.before);
10237 inputblock.cn.push({
10239 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10240 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10244 inputblock.cn.push(input);
10246 if (this.after && typeof(this.after) == 'string') {
10247 inputblock.cn.push({
10249 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10253 if (this.after && typeof(this.after) == 'object') {
10254 this.after = Roo.factory(this.after);
10256 inputblock.cn.push({
10258 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10259 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10263 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10264 inputblock.cls += ' has-feedback';
10265 inputblock.cn.push(feedback);
10270 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10271 tooltip : 'This field is required'
10273 if (Roo.bootstrap.version == 4) {
10276 style : 'display-none'
10279 if (align ==='left' && this.fieldLabel.length) {
10281 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10288 cls : 'control-label col-form-label',
10289 html : this.fieldLabel
10300 var labelCfg = cfg.cn[1];
10301 var contentCfg = cfg.cn[2];
10303 if(this.indicatorpos == 'right'){
10308 cls : 'control-label col-form-label',
10312 html : this.fieldLabel
10326 labelCfg = cfg.cn[0];
10327 contentCfg = cfg.cn[1];
10331 if(this.labelWidth > 12){
10332 labelCfg.style = "width: " + this.labelWidth + 'px';
10335 if(this.labelWidth < 13 && this.labelmd == 0){
10336 this.labelmd = this.labelWidth;
10339 if(this.labellg > 0){
10340 labelCfg.cls += ' col-lg-' + this.labellg;
10341 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10344 if(this.labelmd > 0){
10345 labelCfg.cls += ' col-md-' + this.labelmd;
10346 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10349 if(this.labelsm > 0){
10350 labelCfg.cls += ' col-sm-' + this.labelsm;
10351 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10354 if(this.labelxs > 0){
10355 labelCfg.cls += ' col-xs-' + this.labelxs;
10356 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10360 } else if ( this.fieldLabel.length) {
10365 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10366 tooltip : 'This field is required'
10370 //cls : 'input-group-addon',
10371 html : this.fieldLabel
10379 if(this.indicatorpos == 'right'){
10384 //cls : 'input-group-addon',
10385 html : this.fieldLabel
10390 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10391 tooltip : 'This field is required'
10411 if (this.parentType === 'Navbar' && this.parent().bar) {
10412 cfg.cls += ' navbar-form';
10415 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10416 // on BS4 we do this only if not form
10417 cfg.cls += ' navbar-form';
10425 * return the real input element.
10427 inputEl: function ()
10429 return this.el.select('input.form-control',true).first();
10432 tooltipEl : function()
10434 return this.inputEl();
10437 indicatorEl : function()
10439 if (Roo.bootstrap.version == 4) {
10440 return false; // not enabled in v4 yet.
10443 var indicator = this.el.select('i.roo-required-indicator',true).first();
10453 setDisabled : function(v)
10455 var i = this.inputEl().dom;
10457 i.removeAttribute('disabled');
10461 i.setAttribute('disabled','true');
10463 initEvents : function()
10466 this.inputEl().on("keydown" , this.fireKey, this);
10467 this.inputEl().on("focus", this.onFocus, this);
10468 this.inputEl().on("blur", this.onBlur, this);
10470 this.inputEl().relayEvent('keyup', this);
10472 this.indicator = this.indicatorEl();
10474 if(this.indicator){
10475 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10478 // reference to original value for reset
10479 this.originalValue = this.getValue();
10480 //Roo.form.TextField.superclass.initEvents.call(this);
10481 if(this.validationEvent == 'keyup'){
10482 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10483 this.inputEl().on('keyup', this.filterValidation, this);
10485 else if(this.validationEvent !== false){
10486 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10489 if(this.selectOnFocus){
10490 this.on("focus", this.preFocus, this);
10493 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10494 this.inputEl().on("keypress", this.filterKeys, this);
10496 this.inputEl().relayEvent('keypress', this);
10499 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10500 this.el.on("click", this.autoSize, this);
10503 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10504 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10507 if (typeof(this.before) == 'object') {
10508 this.before.render(this.el.select('.roo-input-before',true).first());
10510 if (typeof(this.after) == 'object') {
10511 this.after.render(this.el.select('.roo-input-after',true).first());
10514 this.inputEl().on('change', this.onChange, this);
10517 filterValidation : function(e){
10518 if(!e.isNavKeyPress()){
10519 this.validationTask.delay(this.validationDelay);
10523 * Validates the field value
10524 * @return {Boolean} True if the value is valid, else false
10526 validate : function(){
10527 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10528 if(this.disabled || this.validateValue(this.getRawValue())){
10533 this.markInvalid();
10539 * Validates a value according to the field's validation rules and marks the field as invalid
10540 * if the validation fails
10541 * @param {Mixed} value The value to validate
10542 * @return {Boolean} True if the value is valid, else false
10544 validateValue : function(value)
10546 if(this.getVisibilityEl().hasClass('hidden')){
10550 if(value.length < 1) { // if it's blank
10551 if(this.allowBlank){
10557 if(value.length < this.minLength){
10560 if(value.length > this.maxLength){
10564 var vt = Roo.form.VTypes;
10565 if(!vt[this.vtype](value, this)){
10569 if(typeof this.validator == "function"){
10570 var msg = this.validator(value);
10574 if (typeof(msg) == 'string') {
10575 this.invalidText = msg;
10579 if(this.regex && !this.regex.test(value)){
10587 fireKey : function(e){
10588 //Roo.log('field ' + e.getKey());
10589 if(e.isNavKeyPress()){
10590 this.fireEvent("specialkey", this, e);
10593 focus : function (selectText){
10595 this.inputEl().focus();
10596 if(selectText === true){
10597 this.inputEl().dom.select();
10603 onFocus : function(){
10604 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10605 // this.el.addClass(this.focusClass);
10607 if(!this.hasFocus){
10608 this.hasFocus = true;
10609 this.startValue = this.getValue();
10610 this.fireEvent("focus", this);
10614 beforeBlur : Roo.emptyFn,
10618 onBlur : function(){
10620 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10621 //this.el.removeClass(this.focusClass);
10623 this.hasFocus = false;
10624 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10627 var v = this.getValue();
10628 if(String(v) !== String(this.startValue)){
10629 this.fireEvent('change', this, v, this.startValue);
10631 this.fireEvent("blur", this);
10634 onChange : function(e)
10636 var v = this.getValue();
10637 if(String(v) !== String(this.startValue)){
10638 this.fireEvent('change', this, v, this.startValue);
10644 * Resets the current field value to the originally loaded value and clears any validation messages
10646 reset : function(){
10647 this.setValue(this.originalValue);
10651 * Returns the name of the field
10652 * @return {Mixed} name The name field
10654 getName: function(){
10658 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10659 * @return {Mixed} value The field value
10661 getValue : function(){
10663 var v = this.inputEl().getValue();
10668 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10669 * @return {Mixed} value The field value
10671 getRawValue : function(){
10672 var v = this.inputEl().getValue();
10678 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10679 * @param {Mixed} value The value to set
10681 setRawValue : function(v){
10682 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10685 selectText : function(start, end){
10686 var v = this.getRawValue();
10688 start = start === undefined ? 0 : start;
10689 end = end === undefined ? v.length : end;
10690 var d = this.inputEl().dom;
10691 if(d.setSelectionRange){
10692 d.setSelectionRange(start, end);
10693 }else if(d.createTextRange){
10694 var range = d.createTextRange();
10695 range.moveStart("character", start);
10696 range.moveEnd("character", v.length-end);
10703 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10704 * @param {Mixed} value The value to set
10706 setValue : function(v){
10709 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10715 processValue : function(value){
10716 if(this.stripCharsRe){
10717 var newValue = value.replace(this.stripCharsRe, '');
10718 if(newValue !== value){
10719 this.setRawValue(newValue);
10726 preFocus : function(){
10728 if(this.selectOnFocus){
10729 this.inputEl().dom.select();
10732 filterKeys : function(e){
10733 var k = e.getKey();
10734 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10737 var c = e.getCharCode(), cc = String.fromCharCode(c);
10738 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10741 if(!this.maskRe.test(cc)){
10746 * Clear any invalid styles/messages for this field
10748 clearInvalid : function(){
10750 if(!this.el || this.preventMark){ // not rendered
10755 this.el.removeClass([this.invalidClass, 'is-invalid']);
10757 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10759 var feedback = this.el.select('.form-control-feedback', true).first();
10762 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10767 if(this.indicator){
10768 this.indicator.removeClass('visible');
10769 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10772 this.fireEvent('valid', this);
10776 * Mark this field as valid
10778 markValid : function()
10780 if(!this.el || this.preventMark){ // not rendered...
10784 this.el.removeClass([this.invalidClass, this.validClass]);
10785 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10787 var feedback = this.el.select('.form-control-feedback', true).first();
10790 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10793 if(this.indicator){
10794 this.indicator.removeClass('visible');
10795 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10802 if(this.allowBlank && !this.getRawValue().length){
10805 if (Roo.bootstrap.version == 3) {
10806 this.el.addClass(this.validClass);
10808 this.inputEl().addClass('is-valid');
10811 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10813 var feedback = this.el.select('.form-control-feedback', true).first();
10816 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10817 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10822 this.fireEvent('valid', this);
10826 * Mark this field as invalid
10827 * @param {String} msg The validation message
10829 markInvalid : function(msg)
10831 if(!this.el || this.preventMark){ // not rendered
10835 this.el.removeClass([this.invalidClass, this.validClass]);
10836 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10838 var feedback = this.el.select('.form-control-feedback', true).first();
10841 this.el.select('.form-control-feedback', true).first().removeClass(
10842 [this.invalidFeedbackClass, this.validFeedbackClass]);
10849 if(this.allowBlank && !this.getRawValue().length){
10853 if(this.indicator){
10854 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10855 this.indicator.addClass('visible');
10857 if (Roo.bootstrap.version == 3) {
10858 this.el.addClass(this.invalidClass);
10860 this.inputEl().addClass('is-invalid');
10865 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10867 var feedback = this.el.select('.form-control-feedback', true).first();
10870 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10872 if(this.getValue().length || this.forceFeedback){
10873 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10880 this.fireEvent('invalid', this, msg);
10883 SafariOnKeyDown : function(event)
10885 // this is a workaround for a password hang bug on chrome/ webkit.
10886 if (this.inputEl().dom.type != 'password') {
10890 var isSelectAll = false;
10892 if(this.inputEl().dom.selectionEnd > 0){
10893 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10895 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10896 event.preventDefault();
10901 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10903 event.preventDefault();
10904 // this is very hacky as keydown always get's upper case.
10906 var cc = String.fromCharCode(event.getCharCode());
10907 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10911 adjustWidth : function(tag, w){
10912 tag = tag.toLowerCase();
10913 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10914 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10915 if(tag == 'input'){
10918 if(tag == 'textarea'){
10921 }else if(Roo.isOpera){
10922 if(tag == 'input'){
10925 if(tag == 'textarea'){
10933 setFieldLabel : function(v)
10935 if(!this.rendered){
10939 if(this.indicatorEl()){
10940 var ar = this.el.select('label > span',true);
10942 if (ar.elements.length) {
10943 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10944 this.fieldLabel = v;
10948 var br = this.el.select('label',true);
10950 if(br.elements.length) {
10951 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10952 this.fieldLabel = v;
10956 Roo.log('Cannot Found any of label > span || label in input');
10960 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10961 this.fieldLabel = v;
10976 * @class Roo.bootstrap.TextArea
10977 * @extends Roo.bootstrap.Input
10978 * Bootstrap TextArea class
10979 * @cfg {Number} cols Specifies the visible width of a text area
10980 * @cfg {Number} rows Specifies the visible number of lines in a text area
10981 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10982 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10983 * @cfg {string} html text
10986 * Create a new TextArea
10987 * @param {Object} config The config object
10990 Roo.bootstrap.TextArea = function(config){
10991 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10995 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11005 getAutoCreate : function(){
11007 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11013 if(this.inputType != 'hidden'){
11014 cfg.cls = 'form-group' //input-group
11022 value : this.value || '',
11023 html: this.html || '',
11024 cls : 'form-control',
11025 placeholder : this.placeholder || ''
11029 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11030 input.maxLength = this.maxLength;
11034 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11038 input.cols = this.cols;
11041 if (this.readOnly) {
11042 input.readonly = true;
11046 input.name = this.name;
11050 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11054 ['xs','sm','md','lg'].map(function(size){
11055 if (settings[size]) {
11056 cfg.cls += ' col-' + size + '-' + settings[size];
11060 var inputblock = input;
11062 if(this.hasFeedback && !this.allowBlank){
11066 cls: 'glyphicon form-control-feedback'
11070 cls : 'has-feedback',
11079 if (this.before || this.after) {
11082 cls : 'input-group',
11086 inputblock.cn.push({
11088 cls : 'input-group-addon',
11093 inputblock.cn.push(input);
11095 if(this.hasFeedback && !this.allowBlank){
11096 inputblock.cls += ' has-feedback';
11097 inputblock.cn.push(feedback);
11101 inputblock.cn.push({
11103 cls : 'input-group-addon',
11110 if (align ==='left' && this.fieldLabel.length) {
11115 cls : 'control-label',
11116 html : this.fieldLabel
11127 if(this.labelWidth > 12){
11128 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11131 if(this.labelWidth < 13 && this.labelmd == 0){
11132 this.labelmd = this.labelWidth;
11135 if(this.labellg > 0){
11136 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11137 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11140 if(this.labelmd > 0){
11141 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11142 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11145 if(this.labelsm > 0){
11146 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11147 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11150 if(this.labelxs > 0){
11151 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11152 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11155 } else if ( this.fieldLabel.length) {
11160 //cls : 'input-group-addon',
11161 html : this.fieldLabel
11179 if (this.disabled) {
11180 input.disabled=true;
11187 * return the real textarea element.
11189 inputEl: function ()
11191 return this.el.select('textarea.form-control',true).first();
11195 * Clear any invalid styles/messages for this field
11197 clearInvalid : function()
11200 if(!this.el || this.preventMark){ // not rendered
11204 var label = this.el.select('label', true).first();
11205 var icon = this.el.select('i.fa-star', true).first();
11210 this.el.removeClass( this.validClass);
11211 this.inputEl().removeClass('is-invalid');
11213 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11215 var feedback = this.el.select('.form-control-feedback', true).first();
11218 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11223 this.fireEvent('valid', this);
11227 * Mark this field as valid
11229 markValid : function()
11231 if(!this.el || this.preventMark){ // not rendered
11235 this.el.removeClass([this.invalidClass, this.validClass]);
11236 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11238 var feedback = this.el.select('.form-control-feedback', true).first();
11241 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11244 if(this.disabled || this.allowBlank){
11248 var label = this.el.select('label', true).first();
11249 var icon = this.el.select('i.fa-star', true).first();
11254 if (Roo.bootstrap.version == 3) {
11255 this.el.addClass(this.validClass);
11257 this.inputEl().addClass('is-valid');
11261 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11263 var feedback = this.el.select('.form-control-feedback', true).first();
11266 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11267 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11272 this.fireEvent('valid', this);
11276 * Mark this field as invalid
11277 * @param {String} msg The validation message
11279 markInvalid : function(msg)
11281 if(!this.el || this.preventMark){ // not rendered
11285 this.el.removeClass([this.invalidClass, this.validClass]);
11286 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11288 var feedback = this.el.select('.form-control-feedback', true).first();
11291 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11294 if(this.disabled || this.allowBlank){
11298 var label = this.el.select('label', true).first();
11299 var icon = this.el.select('i.fa-star', true).first();
11301 if(!this.getValue().length && label && !icon){
11302 this.el.createChild({
11304 cls : 'text-danger fa fa-lg fa-star',
11305 tooltip : 'This field is required',
11306 style : 'margin-right:5px;'
11310 if (Roo.bootstrap.version == 3) {
11311 this.el.addClass(this.invalidClass);
11313 this.inputEl().addClass('is-invalid');
11316 // fixme ... this may be depricated need to test..
11317 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11319 var feedback = this.el.select('.form-control-feedback', true).first();
11322 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11324 if(this.getValue().length || this.forceFeedback){
11325 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11332 this.fireEvent('invalid', this, msg);
11340 * trigger field - base class for combo..
11345 * @class Roo.bootstrap.TriggerField
11346 * @extends Roo.bootstrap.Input
11347 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11348 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11349 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11350 * for which you can provide a custom implementation. For example:
11352 var trigger = new Roo.bootstrap.TriggerField();
11353 trigger.onTriggerClick = myTriggerFn;
11354 trigger.applyTo('my-field');
11357 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11358 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11359 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11360 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11361 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11364 * Create a new TriggerField.
11365 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11366 * to the base TextField)
11368 Roo.bootstrap.TriggerField = function(config){
11369 this.mimicing = false;
11370 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11373 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11375 * @cfg {String} triggerClass A CSS class to apply to the trigger
11378 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11383 * @cfg {Boolean} removable (true|false) special filter default false
11387 /** @cfg {Boolean} grow @hide */
11388 /** @cfg {Number} growMin @hide */
11389 /** @cfg {Number} growMax @hide */
11395 autoSize: Roo.emptyFn,
11399 deferHeight : true,
11402 actionMode : 'wrap',
11407 getAutoCreate : function(){
11409 var align = this.labelAlign || this.parentLabelAlign();
11414 cls: 'form-group' //input-group
11421 type : this.inputType,
11422 cls : 'form-control',
11423 autocomplete: 'new-password',
11424 placeholder : this.placeholder || ''
11428 input.name = this.name;
11431 input.cls += ' input-' + this.size;
11434 if (this.disabled) {
11435 input.disabled=true;
11438 var inputblock = input;
11440 if(this.hasFeedback && !this.allowBlank){
11444 cls: 'glyphicon form-control-feedback'
11447 if(this.removable && !this.editable && !this.tickable){
11449 cls : 'has-feedback',
11455 cls : 'roo-combo-removable-btn close'
11462 cls : 'has-feedback',
11471 if(this.removable && !this.editable && !this.tickable){
11473 cls : 'roo-removable',
11479 cls : 'roo-combo-removable-btn close'
11486 if (this.before || this.after) {
11489 cls : 'input-group',
11493 inputblock.cn.push({
11495 cls : 'input-group-addon input-group-prepend input-group-text',
11500 inputblock.cn.push(input);
11502 if(this.hasFeedback && !this.allowBlank){
11503 inputblock.cls += ' has-feedback';
11504 inputblock.cn.push(feedback);
11508 inputblock.cn.push({
11510 cls : 'input-group-addon input-group-append input-group-text',
11519 var ibwrap = inputblock;
11524 cls: 'roo-select2-choices',
11528 cls: 'roo-select2-search-field',
11540 cls: 'roo-select2-container input-group',
11545 cls: 'form-hidden-field'
11551 if(!this.multiple && this.showToggleBtn){
11557 if (this.caret != false) {
11560 cls: 'fa fa-' + this.caret
11567 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11569 Roo.bootstrap.version == 3 ? caret : '',
11572 cls: 'combobox-clear',
11586 combobox.cls += ' roo-select2-container-multi';
11590 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11591 tooltip : 'This field is required'
11593 if (Roo.bootstrap.version == 4) {
11596 style : 'display:none'
11601 if (align ==='left' && this.fieldLabel.length) {
11603 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11610 cls : 'control-label',
11611 html : this.fieldLabel
11623 var labelCfg = cfg.cn[1];
11624 var contentCfg = cfg.cn[2];
11626 if(this.indicatorpos == 'right'){
11631 cls : 'control-label',
11635 html : this.fieldLabel
11649 labelCfg = cfg.cn[0];
11650 contentCfg = cfg.cn[1];
11653 if(this.labelWidth > 12){
11654 labelCfg.style = "width: " + this.labelWidth + 'px';
11657 if(this.labelWidth < 13 && this.labelmd == 0){
11658 this.labelmd = this.labelWidth;
11661 if(this.labellg > 0){
11662 labelCfg.cls += ' col-lg-' + this.labellg;
11663 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11666 if(this.labelmd > 0){
11667 labelCfg.cls += ' col-md-' + this.labelmd;
11668 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11671 if(this.labelsm > 0){
11672 labelCfg.cls += ' col-sm-' + this.labelsm;
11673 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11676 if(this.labelxs > 0){
11677 labelCfg.cls += ' col-xs-' + this.labelxs;
11678 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11681 } else if ( this.fieldLabel.length) {
11682 // Roo.log(" label");
11687 //cls : 'input-group-addon',
11688 html : this.fieldLabel
11696 if(this.indicatorpos == 'right'){
11704 html : this.fieldLabel
11718 // Roo.log(" no label && no align");
11725 ['xs','sm','md','lg'].map(function(size){
11726 if (settings[size]) {
11727 cfg.cls += ' col-' + size + '-' + settings[size];
11738 onResize : function(w, h){
11739 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11740 // if(typeof w == 'number'){
11741 // var x = w - this.trigger.getWidth();
11742 // this.inputEl().setWidth(this.adjustWidth('input', x));
11743 // this.trigger.setStyle('left', x+'px');
11748 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11751 getResizeEl : function(){
11752 return this.inputEl();
11756 getPositionEl : function(){
11757 return this.inputEl();
11761 alignErrorIcon : function(){
11762 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11766 initEvents : function(){
11770 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11771 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11772 if(!this.multiple && this.showToggleBtn){
11773 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11774 if(this.hideTrigger){
11775 this.trigger.setDisplayed(false);
11777 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11781 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11784 if(this.removable && !this.editable && !this.tickable){
11785 var close = this.closeTriggerEl();
11788 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11789 close.on('click', this.removeBtnClick, this, close);
11793 //this.trigger.addClassOnOver('x-form-trigger-over');
11794 //this.trigger.addClassOnClick('x-form-trigger-click');
11797 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11801 closeTriggerEl : function()
11803 var close = this.el.select('.roo-combo-removable-btn', true).first();
11804 return close ? close : false;
11807 removeBtnClick : function(e, h, el)
11809 e.preventDefault();
11811 if(this.fireEvent("remove", this) !== false){
11813 this.fireEvent("afterremove", this)
11817 createList : function()
11819 this.list = Roo.get(document.body).createChild({
11820 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11821 cls: 'typeahead typeahead-long dropdown-menu',
11822 style: 'display:none'
11825 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11830 initTrigger : function(){
11835 onDestroy : function(){
11837 this.trigger.removeAllListeners();
11838 // this.trigger.remove();
11841 // this.wrap.remove();
11843 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11847 onFocus : function(){
11848 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11850 if(!this.mimicing){
11851 this.wrap.addClass('x-trigger-wrap-focus');
11852 this.mimicing = true;
11853 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11854 if(this.monitorTab){
11855 this.el.on("keydown", this.checkTab, this);
11862 checkTab : function(e){
11863 if(e.getKey() == e.TAB){
11864 this.triggerBlur();
11869 onBlur : function(){
11874 mimicBlur : function(e, t){
11876 if(!this.wrap.contains(t) && this.validateBlur()){
11877 this.triggerBlur();
11883 triggerBlur : function(){
11884 this.mimicing = false;
11885 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11886 if(this.monitorTab){
11887 this.el.un("keydown", this.checkTab, this);
11889 //this.wrap.removeClass('x-trigger-wrap-focus');
11890 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11894 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11895 validateBlur : function(e, t){
11900 onDisable : function(){
11901 this.inputEl().dom.disabled = true;
11902 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11904 // this.wrap.addClass('x-item-disabled');
11909 onEnable : function(){
11910 this.inputEl().dom.disabled = false;
11911 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11913 // this.el.removeClass('x-item-disabled');
11918 onShow : function(){
11919 var ae = this.getActionEl();
11922 ae.dom.style.display = '';
11923 ae.dom.style.visibility = 'visible';
11929 onHide : function(){
11930 var ae = this.getActionEl();
11931 ae.dom.style.display = 'none';
11935 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11936 * by an implementing function.
11938 * @param {EventObject} e
11940 onTriggerClick : Roo.emptyFn
11944 * Ext JS Library 1.1.1
11945 * Copyright(c) 2006-2007, Ext JS, LLC.
11947 * Originally Released Under LGPL - original licence link has changed is not relivant.
11950 * <script type="text/javascript">
11955 * @class Roo.data.SortTypes
11957 * Defines the default sorting (casting?) comparison functions used when sorting data.
11959 Roo.data.SortTypes = {
11961 * Default sort that does nothing
11962 * @param {Mixed} s The value being converted
11963 * @return {Mixed} The comparison value
11965 none : function(s){
11970 * The regular expression used to strip tags
11974 stripTagsRE : /<\/?[^>]+>/gi,
11977 * Strips all HTML tags to sort on text only
11978 * @param {Mixed} s The value being converted
11979 * @return {String} The comparison value
11981 asText : function(s){
11982 return String(s).replace(this.stripTagsRE, "");
11986 * Strips all HTML tags to sort on text only - Case insensitive
11987 * @param {Mixed} s The value being converted
11988 * @return {String} The comparison value
11990 asUCText : function(s){
11991 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11995 * Case insensitive string
11996 * @param {Mixed} s The value being converted
11997 * @return {String} The comparison value
11999 asUCString : function(s) {
12000 return String(s).toUpperCase();
12005 * @param {Mixed} s The value being converted
12006 * @return {Number} The comparison value
12008 asDate : function(s) {
12012 if(s instanceof Date){
12013 return s.getTime();
12015 return Date.parse(String(s));
12020 * @param {Mixed} s The value being converted
12021 * @return {Float} The comparison value
12023 asFloat : function(s) {
12024 var val = parseFloat(String(s).replace(/,/g, ""));
12033 * @param {Mixed} s The value being converted
12034 * @return {Number} The comparison value
12036 asInt : function(s) {
12037 var val = parseInt(String(s).replace(/,/g, ""));
12045 * Ext JS Library 1.1.1
12046 * Copyright(c) 2006-2007, Ext JS, LLC.
12048 * Originally Released Under LGPL - original licence link has changed is not relivant.
12051 * <script type="text/javascript">
12055 * @class Roo.data.Record
12056 * Instances of this class encapsulate both record <em>definition</em> information, and record
12057 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12058 * to access Records cached in an {@link Roo.data.Store} object.<br>
12060 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12061 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12064 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12066 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12067 * {@link #create}. The parameters are the same.
12068 * @param {Array} data An associative Array of data values keyed by the field name.
12069 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12070 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12071 * not specified an integer id is generated.
12073 Roo.data.Record = function(data, id){
12074 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12079 * Generate a constructor for a specific record layout.
12080 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12081 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12082 * Each field definition object may contain the following properties: <ul>
12083 * <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,
12084 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12085 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12086 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12087 * is being used, then this is a string containing the javascript expression to reference the data relative to
12088 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12089 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12090 * this may be omitted.</p></li>
12091 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12092 * <ul><li>auto (Default, implies no conversion)</li>
12097 * <li>date</li></ul></p></li>
12098 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12099 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12100 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12101 * by the Reader into an object that will be stored in the Record. It is passed the
12102 * following parameters:<ul>
12103 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12105 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12107 * <br>usage:<br><pre><code>
12108 var TopicRecord = Roo.data.Record.create(
12109 {name: 'title', mapping: 'topic_title'},
12110 {name: 'author', mapping: 'username'},
12111 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12112 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12113 {name: 'lastPoster', mapping: 'user2'},
12114 {name: 'excerpt', mapping: 'post_text'}
12117 var myNewRecord = new TopicRecord({
12118 title: 'Do my job please',
12121 lastPost: new Date(),
12122 lastPoster: 'Animal',
12123 excerpt: 'No way dude!'
12125 myStore.add(myNewRecord);
12130 Roo.data.Record.create = function(o){
12131 var f = function(){
12132 f.superclass.constructor.apply(this, arguments);
12134 Roo.extend(f, Roo.data.Record);
12135 var p = f.prototype;
12136 p.fields = new Roo.util.MixedCollection(false, function(field){
12139 for(var i = 0, len = o.length; i < len; i++){
12140 p.fields.add(new Roo.data.Field(o[i]));
12142 f.getField = function(name){
12143 return p.fields.get(name);
12148 Roo.data.Record.AUTO_ID = 1000;
12149 Roo.data.Record.EDIT = 'edit';
12150 Roo.data.Record.REJECT = 'reject';
12151 Roo.data.Record.COMMIT = 'commit';
12153 Roo.data.Record.prototype = {
12155 * Readonly flag - true if this record has been modified.
12164 join : function(store){
12165 this.store = store;
12169 * Set the named field to the specified value.
12170 * @param {String} name The name of the field to set.
12171 * @param {Object} value The value to set the field to.
12173 set : function(name, value){
12174 if(this.data[name] == value){
12178 if(!this.modified){
12179 this.modified = {};
12181 if(typeof this.modified[name] == 'undefined'){
12182 this.modified[name] = this.data[name];
12184 this.data[name] = value;
12185 if(!this.editing && this.store){
12186 this.store.afterEdit(this);
12191 * Get the value of the named field.
12192 * @param {String} name The name of the field to get the value of.
12193 * @return {Object} The value of the field.
12195 get : function(name){
12196 return this.data[name];
12200 beginEdit : function(){
12201 this.editing = true;
12202 this.modified = {};
12206 cancelEdit : function(){
12207 this.editing = false;
12208 delete this.modified;
12212 endEdit : function(){
12213 this.editing = false;
12214 if(this.dirty && this.store){
12215 this.store.afterEdit(this);
12220 * Usually called by the {@link Roo.data.Store} which owns the Record.
12221 * Rejects all changes made to the Record since either creation, or the last commit operation.
12222 * Modified fields are reverted to their original values.
12224 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12225 * of reject operations.
12227 reject : function(){
12228 var m = this.modified;
12230 if(typeof m[n] != "function"){
12231 this.data[n] = m[n];
12234 this.dirty = false;
12235 delete this.modified;
12236 this.editing = false;
12238 this.store.afterReject(this);
12243 * Usually called by the {@link Roo.data.Store} which owns the Record.
12244 * Commits all changes made to the Record since either creation, or the last commit operation.
12246 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12247 * of commit operations.
12249 commit : function(){
12250 this.dirty = false;
12251 delete this.modified;
12252 this.editing = false;
12254 this.store.afterCommit(this);
12259 hasError : function(){
12260 return this.error != null;
12264 clearError : function(){
12269 * Creates a copy of this record.
12270 * @param {String} id (optional) A new record id if you don't want to use this record's id
12273 copy : function(newId) {
12274 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12278 * Ext JS Library 1.1.1
12279 * Copyright(c) 2006-2007, Ext JS, LLC.
12281 * Originally Released Under LGPL - original licence link has changed is not relivant.
12284 * <script type="text/javascript">
12290 * @class Roo.data.Store
12291 * @extends Roo.util.Observable
12292 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12293 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12295 * 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
12296 * has no knowledge of the format of the data returned by the Proxy.<br>
12298 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12299 * instances from the data object. These records are cached and made available through accessor functions.
12301 * Creates a new Store.
12302 * @param {Object} config A config object containing the objects needed for the Store to access data,
12303 * and read the data into Records.
12305 Roo.data.Store = function(config){
12306 this.data = new Roo.util.MixedCollection(false);
12307 this.data.getKey = function(o){
12310 this.baseParams = {};
12312 this.paramNames = {
12317 "multisort" : "_multisort"
12320 if(config && config.data){
12321 this.inlineData = config.data;
12322 delete config.data;
12325 Roo.apply(this, config);
12327 if(this.reader){ // reader passed
12328 this.reader = Roo.factory(this.reader, Roo.data);
12329 this.reader.xmodule = this.xmodule || false;
12330 if(!this.recordType){
12331 this.recordType = this.reader.recordType;
12333 if(this.reader.onMetaChange){
12334 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12338 if(this.recordType){
12339 this.fields = this.recordType.prototype.fields;
12341 this.modified = [];
12345 * @event datachanged
12346 * Fires when the data cache has changed, and a widget which is using this Store
12347 * as a Record cache should refresh its view.
12348 * @param {Store} this
12350 datachanged : true,
12352 * @event metachange
12353 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12354 * @param {Store} this
12355 * @param {Object} meta The JSON metadata
12360 * Fires when Records have been added to the Store
12361 * @param {Store} this
12362 * @param {Roo.data.Record[]} records The array of Records added
12363 * @param {Number} index The index at which the record(s) were added
12368 * Fires when a Record has been removed from the Store
12369 * @param {Store} this
12370 * @param {Roo.data.Record} record The Record that was removed
12371 * @param {Number} index The index at which the record was removed
12376 * Fires when a Record has been updated
12377 * @param {Store} this
12378 * @param {Roo.data.Record} record The Record that was updated
12379 * @param {String} operation The update operation being performed. Value may be one of:
12381 Roo.data.Record.EDIT
12382 Roo.data.Record.REJECT
12383 Roo.data.Record.COMMIT
12389 * Fires when the data cache has been cleared.
12390 * @param {Store} this
12394 * @event beforeload
12395 * Fires before a request is made for a new data object. If the beforeload handler returns false
12396 * the load action will be canceled.
12397 * @param {Store} this
12398 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12402 * @event beforeloadadd
12403 * Fires after a new set of Records has been loaded.
12404 * @param {Store} this
12405 * @param {Roo.data.Record[]} records The Records that were loaded
12406 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12408 beforeloadadd : true,
12411 * Fires after a new set of Records has been loaded, before they are added to the store.
12412 * @param {Store} this
12413 * @param {Roo.data.Record[]} records The Records that were loaded
12414 * @param {Object} options The loading options that were specified (see {@link #load} for details)
12415 * @params {Object} return from reader
12419 * @event loadexception
12420 * Fires if an exception occurs in the Proxy during loading.
12421 * Called with the signature of the Proxy's "loadexception" event.
12422 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12425 * @param {Object} return from JsonData.reader() - success, totalRecords, records
12426 * @param {Object} load options
12427 * @param {Object} jsonData from your request (normally this contains the Exception)
12429 loadexception : true
12433 this.proxy = Roo.factory(this.proxy, Roo.data);
12434 this.proxy.xmodule = this.xmodule || false;
12435 this.relayEvents(this.proxy, ["loadexception"]);
12437 this.sortToggle = {};
12438 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12440 Roo.data.Store.superclass.constructor.call(this);
12442 if(this.inlineData){
12443 this.loadData(this.inlineData);
12444 delete this.inlineData;
12448 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12450 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
12451 * without a remote query - used by combo/forms at present.
12455 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12458 * @cfg {Array} data Inline data to be loaded when the store is initialized.
12461 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12462 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12465 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12466 * on any HTTP request
12469 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12472 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12476 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12477 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12479 remoteSort : false,
12482 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12483 * loaded or when a record is removed. (defaults to false).
12485 pruneModifiedRecords : false,
12488 lastOptions : null,
12491 * Add Records to the Store and fires the add event.
12492 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12494 add : function(records){
12495 records = [].concat(records);
12496 for(var i = 0, len = records.length; i < len; i++){
12497 records[i].join(this);
12499 var index = this.data.length;
12500 this.data.addAll(records);
12501 this.fireEvent("add", this, records, index);
12505 * Remove a Record from the Store and fires the remove event.
12506 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12508 remove : function(record){
12509 var index = this.data.indexOf(record);
12510 this.data.removeAt(index);
12512 if(this.pruneModifiedRecords){
12513 this.modified.remove(record);
12515 this.fireEvent("remove", this, record, index);
12519 * Remove all Records from the Store and fires the clear event.
12521 removeAll : function(){
12523 if(this.pruneModifiedRecords){
12524 this.modified = [];
12526 this.fireEvent("clear", this);
12530 * Inserts Records to the Store at the given index and fires the add event.
12531 * @param {Number} index The start index at which to insert the passed Records.
12532 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12534 insert : function(index, records){
12535 records = [].concat(records);
12536 for(var i = 0, len = records.length; i < len; i++){
12537 this.data.insert(index, records[i]);
12538 records[i].join(this);
12540 this.fireEvent("add", this, records, index);
12544 * Get the index within the cache of the passed Record.
12545 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12546 * @return {Number} The index of the passed Record. Returns -1 if not found.
12548 indexOf : function(record){
12549 return this.data.indexOf(record);
12553 * Get the index within the cache of the Record with the passed id.
12554 * @param {String} id The id of the Record to find.
12555 * @return {Number} The index of the Record. Returns -1 if not found.
12557 indexOfId : function(id){
12558 return this.data.indexOfKey(id);
12562 * Get the Record with the specified id.
12563 * @param {String} id The id of the Record to find.
12564 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12566 getById : function(id){
12567 return this.data.key(id);
12571 * Get the Record at the specified index.
12572 * @param {Number} index The index of the Record to find.
12573 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12575 getAt : function(index){
12576 return this.data.itemAt(index);
12580 * Returns a range of Records between specified indices.
12581 * @param {Number} startIndex (optional) The starting index (defaults to 0)
12582 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12583 * @return {Roo.data.Record[]} An array of Records
12585 getRange : function(start, end){
12586 return this.data.getRange(start, end);
12590 storeOptions : function(o){
12591 o = Roo.apply({}, o);
12594 this.lastOptions = o;
12598 * Loads the Record cache from the configured Proxy using the configured Reader.
12600 * If using remote paging, then the first load call must specify the <em>start</em>
12601 * and <em>limit</em> properties in the options.params property to establish the initial
12602 * position within the dataset, and the number of Records to cache on each read from the Proxy.
12604 * <strong>It is important to note that for remote data sources, loading is asynchronous,
12605 * and this call will return before the new data has been loaded. Perform any post-processing
12606 * in a callback function, or in a "load" event handler.</strong>
12608 * @param {Object} options An object containing properties which control loading options:<ul>
12609 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12610 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12611 * passed the following arguments:<ul>
12612 * <li>r : Roo.data.Record[]</li>
12613 * <li>options: Options object from the load call</li>
12614 * <li>success: Boolean success indicator</li></ul></li>
12615 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12616 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12619 load : function(options){
12620 options = options || {};
12621 if(this.fireEvent("beforeload", this, options) !== false){
12622 this.storeOptions(options);
12623 var p = Roo.apply(options.params || {}, this.baseParams);
12624 // if meta was not loaded from remote source.. try requesting it.
12625 if (!this.reader.metaFromRemote) {
12626 p._requestMeta = 1;
12628 if(this.sortInfo && this.remoteSort){
12629 var pn = this.paramNames;
12630 p[pn["sort"]] = this.sortInfo.field;
12631 p[pn["dir"]] = this.sortInfo.direction;
12633 if (this.multiSort) {
12634 var pn = this.paramNames;
12635 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12638 this.proxy.load(p, this.reader, this.loadRecords, this, options);
12643 * Reloads the Record cache from the configured Proxy using the configured Reader and
12644 * the options from the last load operation performed.
12645 * @param {Object} options (optional) An object containing properties which may override the options
12646 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12647 * the most recently used options are reused).
12649 reload : function(options){
12650 this.load(Roo.applyIf(options||{}, this.lastOptions));
12654 // Called as a callback by the Reader during a load operation.
12655 loadRecords : function(o, options, success){
12656 if(!o || success === false){
12657 if(success !== false){
12658 this.fireEvent("load", this, [], options, o);
12660 if(options.callback){
12661 options.callback.call(options.scope || this, [], options, false);
12665 // if data returned failure - throw an exception.
12666 if (o.success === false) {
12667 // show a message if no listener is registered.
12668 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12669 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12671 // loadmask wil be hooked into this..
12672 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12675 var r = o.records, t = o.totalRecords || r.length;
12677 this.fireEvent("beforeloadadd", this, r, options, o);
12679 if(!options || options.add !== true){
12680 if(this.pruneModifiedRecords){
12681 this.modified = [];
12683 for(var i = 0, len = r.length; i < len; i++){
12687 this.data = this.snapshot;
12688 delete this.snapshot;
12691 this.data.addAll(r);
12692 this.totalLength = t;
12694 this.fireEvent("datachanged", this);
12696 this.totalLength = Math.max(t, this.data.length+r.length);
12700 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12702 var e = new Roo.data.Record({});
12704 e.set(this.parent.displayField, this.parent.emptyTitle);
12705 e.set(this.parent.valueField, '');
12710 this.fireEvent("load", this, r, options, o);
12711 if(options.callback){
12712 options.callback.call(options.scope || this, r, options, true);
12718 * Loads data from a passed data block. A Reader which understands the format of the data
12719 * must have been configured in the constructor.
12720 * @param {Object} data The data block from which to read the Records. The format of the data expected
12721 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12722 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12724 loadData : function(o, append){
12725 var r = this.reader.readRecords(o);
12726 this.loadRecords(r, {add: append}, true);
12730 * using 'cn' the nested child reader read the child array into it's child stores.
12731 * @param {Object} rec The record with a 'children array
12733 loadDataFromChildren : function(rec)
12735 this.loadData(this.reader.toLoadData(rec));
12740 * Gets the number of cached records.
12742 * <em>If using paging, this may not be the total size of the dataset. If the data object
12743 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12744 * the data set size</em>
12746 getCount : function(){
12747 return this.data.length || 0;
12751 * Gets the total number of records in the dataset as returned by the server.
12753 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12754 * the dataset size</em>
12756 getTotalCount : function(){
12757 return this.totalLength || 0;
12761 * Returns the sort state of the Store as an object with two properties:
12763 field {String} The name of the field by which the Records are sorted
12764 direction {String} The sort order, "ASC" or "DESC"
12767 getSortState : function(){
12768 return this.sortInfo;
12772 applySort : function(){
12773 if(this.sortInfo && !this.remoteSort){
12774 var s = this.sortInfo, f = s.field;
12775 var st = this.fields.get(f).sortType;
12776 var fn = function(r1, r2){
12777 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12778 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12780 this.data.sort(s.direction, fn);
12781 if(this.snapshot && this.snapshot != this.data){
12782 this.snapshot.sort(s.direction, fn);
12788 * Sets the default sort column and order to be used by the next load operation.
12789 * @param {String} fieldName The name of the field to sort by.
12790 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12792 setDefaultSort : function(field, dir){
12793 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12797 * Sort the Records.
12798 * If remote sorting is used, the sort is performed on the server, and the cache is
12799 * reloaded. If local sorting is used, the cache is sorted internally.
12800 * @param {String} fieldName The name of the field to sort by.
12801 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12803 sort : function(fieldName, dir){
12804 var f = this.fields.get(fieldName);
12806 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12808 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12809 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12814 this.sortToggle[f.name] = dir;
12815 this.sortInfo = {field: f.name, direction: dir};
12816 if(!this.remoteSort){
12818 this.fireEvent("datachanged", this);
12820 this.load(this.lastOptions);
12825 * Calls the specified function for each of the Records in the cache.
12826 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12827 * Returning <em>false</em> aborts and exits the iteration.
12828 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12830 each : function(fn, scope){
12831 this.data.each(fn, scope);
12835 * Gets all records modified since the last commit. Modified records are persisted across load operations
12836 * (e.g., during paging).
12837 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12839 getModifiedRecords : function(){
12840 return this.modified;
12844 createFilterFn : function(property, value, anyMatch){
12845 if(!value.exec){ // not a regex
12846 value = String(value);
12847 if(value.length == 0){
12850 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12852 return function(r){
12853 return value.test(r.data[property]);
12858 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12859 * @param {String} property A field on your records
12860 * @param {Number} start The record index to start at (defaults to 0)
12861 * @param {Number} end The last record index to include (defaults to length - 1)
12862 * @return {Number} The sum
12864 sum : function(property, start, end){
12865 var rs = this.data.items, v = 0;
12866 start = start || 0;
12867 end = (end || end === 0) ? end : rs.length-1;
12869 for(var i = start; i <= end; i++){
12870 v += (rs[i].data[property] || 0);
12876 * Filter the records by a specified property.
12877 * @param {String} field A field on your records
12878 * @param {String/RegExp} value Either a string that the field
12879 * should start with or a RegExp to test against the field
12880 * @param {Boolean} anyMatch True to match any part not just the beginning
12882 filter : function(property, value, anyMatch){
12883 var fn = this.createFilterFn(property, value, anyMatch);
12884 return fn ? this.filterBy(fn) : this.clearFilter();
12888 * Filter by a function. The specified function will be called with each
12889 * record in this data source. If the function returns true the record is included,
12890 * otherwise it is filtered.
12891 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12892 * @param {Object} scope (optional) The scope of the function (defaults to this)
12894 filterBy : function(fn, scope){
12895 this.snapshot = this.snapshot || this.data;
12896 this.data = this.queryBy(fn, scope||this);
12897 this.fireEvent("datachanged", this);
12901 * Query the records by a specified property.
12902 * @param {String} field A field on your records
12903 * @param {String/RegExp} value Either a string that the field
12904 * should start with or a RegExp to test against the field
12905 * @param {Boolean} anyMatch True to match any part not just the beginning
12906 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12908 query : function(property, value, anyMatch){
12909 var fn = this.createFilterFn(property, value, anyMatch);
12910 return fn ? this.queryBy(fn) : this.data.clone();
12914 * Query by a function. The specified function will be called with each
12915 * record in this data source. If the function returns true the record is included
12917 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12918 * @param {Object} scope (optional) The scope of the function (defaults to this)
12919 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12921 queryBy : function(fn, scope){
12922 var data = this.snapshot || this.data;
12923 return data.filterBy(fn, scope||this);
12927 * Collects unique values for a particular dataIndex from this store.
12928 * @param {String} dataIndex The property to collect
12929 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12930 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12931 * @return {Array} An array of the unique values
12933 collect : function(dataIndex, allowNull, bypassFilter){
12934 var d = (bypassFilter === true && this.snapshot) ?
12935 this.snapshot.items : this.data.items;
12936 var v, sv, r = [], l = {};
12937 for(var i = 0, len = d.length; i < len; i++){
12938 v = d[i].data[dataIndex];
12940 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12949 * Revert to a view of the Record cache with no filtering applied.
12950 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12952 clearFilter : function(suppressEvent){
12953 if(this.snapshot && this.snapshot != this.data){
12954 this.data = this.snapshot;
12955 delete this.snapshot;
12956 if(suppressEvent !== true){
12957 this.fireEvent("datachanged", this);
12963 afterEdit : function(record){
12964 if(this.modified.indexOf(record) == -1){
12965 this.modified.push(record);
12967 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12971 afterReject : function(record){
12972 this.modified.remove(record);
12973 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12977 afterCommit : function(record){
12978 this.modified.remove(record);
12979 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12983 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12984 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12986 commitChanges : function(){
12987 var m = this.modified.slice(0);
12988 this.modified = [];
12989 for(var i = 0, len = m.length; i < len; i++){
12995 * Cancel outstanding changes on all changed records.
12997 rejectChanges : function(){
12998 var m = this.modified.slice(0);
12999 this.modified = [];
13000 for(var i = 0, len = m.length; i < len; i++){
13005 onMetaChange : function(meta, rtype, o){
13006 this.recordType = rtype;
13007 this.fields = rtype.prototype.fields;
13008 delete this.snapshot;
13009 this.sortInfo = meta.sortInfo || this.sortInfo;
13010 this.modified = [];
13011 this.fireEvent('metachange', this, this.reader.meta);
13014 moveIndex : function(data, type)
13016 var index = this.indexOf(data);
13018 var newIndex = index + type;
13022 this.insert(newIndex, data);
13027 * Ext JS Library 1.1.1
13028 * Copyright(c) 2006-2007, Ext JS, LLC.
13030 * Originally Released Under LGPL - original licence link has changed is not relivant.
13033 * <script type="text/javascript">
13037 * @class Roo.data.SimpleStore
13038 * @extends Roo.data.Store
13039 * Small helper class to make creating Stores from Array data easier.
13040 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13041 * @cfg {Array} fields An array of field definition objects, or field name strings.
13042 * @cfg {Object} an existing reader (eg. copied from another store)
13043 * @cfg {Array} data The multi-dimensional array of data
13045 * @param {Object} config
13047 Roo.data.SimpleStore = function(config)
13049 Roo.data.SimpleStore.superclass.constructor.call(this, {
13051 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13054 Roo.data.Record.create(config.fields)
13056 proxy : new Roo.data.MemoryProxy(config.data)
13060 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13062 * Ext JS Library 1.1.1
13063 * Copyright(c) 2006-2007, Ext JS, LLC.
13065 * Originally Released Under LGPL - original licence link has changed is not relivant.
13068 * <script type="text/javascript">
13073 * @extends Roo.data.Store
13074 * @class Roo.data.JsonStore
13075 * Small helper class to make creating Stores for JSON data easier. <br/>
13077 var store = new Roo.data.JsonStore({
13078 url: 'get-images.php',
13080 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13083 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13084 * JsonReader and HttpProxy (unless inline data is provided).</b>
13085 * @cfg {Array} fields An array of field definition objects, or field name strings.
13087 * @param {Object} config
13089 Roo.data.JsonStore = function(c){
13090 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13091 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13092 reader: new Roo.data.JsonReader(c, c.fields)
13095 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13097 * Ext JS Library 1.1.1
13098 * Copyright(c) 2006-2007, Ext JS, LLC.
13100 * Originally Released Under LGPL - original licence link has changed is not relivant.
13103 * <script type="text/javascript">
13107 Roo.data.Field = function(config){
13108 if(typeof config == "string"){
13109 config = {name: config};
13111 Roo.apply(this, config);
13114 this.type = "auto";
13117 var st = Roo.data.SortTypes;
13118 // named sortTypes are supported, here we look them up
13119 if(typeof this.sortType == "string"){
13120 this.sortType = st[this.sortType];
13123 // set default sortType for strings and dates
13124 if(!this.sortType){
13127 this.sortType = st.asUCString;
13130 this.sortType = st.asDate;
13133 this.sortType = st.none;
13138 var stripRe = /[\$,%]/g;
13140 // prebuilt conversion function for this field, instead of
13141 // switching every time we're reading a value
13143 var cv, dateFormat = this.dateFormat;
13148 cv = function(v){ return v; };
13151 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13155 return v !== undefined && v !== null && v !== '' ?
13156 parseInt(String(v).replace(stripRe, ""), 10) : '';
13161 return v !== undefined && v !== null && v !== '' ?
13162 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13167 cv = function(v){ return v === true || v === "true" || v == 1; };
13174 if(v instanceof Date){
13178 if(dateFormat == "timestamp"){
13179 return new Date(v*1000);
13181 return Date.parseDate(v, dateFormat);
13183 var parsed = Date.parse(v);
13184 return parsed ? new Date(parsed) : null;
13193 Roo.data.Field.prototype = {
13201 * Ext JS Library 1.1.1
13202 * Copyright(c) 2006-2007, Ext JS, LLC.
13204 * Originally Released Under LGPL - original licence link has changed is not relivant.
13207 * <script type="text/javascript">
13210 // Base class for reading structured data from a data source. This class is intended to be
13211 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13214 * @class Roo.data.DataReader
13215 * Base class for reading structured data from a data source. This class is intended to be
13216 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13219 Roo.data.DataReader = function(meta, recordType){
13223 this.recordType = recordType instanceof Array ?
13224 Roo.data.Record.create(recordType) : recordType;
13227 Roo.data.DataReader.prototype = {
13230 readerType : 'Data',
13232 * Create an empty record
13233 * @param {Object} data (optional) - overlay some values
13234 * @return {Roo.data.Record} record created.
13236 newRow : function(d) {
13238 this.recordType.prototype.fields.each(function(c) {
13240 case 'int' : da[c.name] = 0; break;
13241 case 'date' : da[c.name] = new Date(); break;
13242 case 'float' : da[c.name] = 0.0; break;
13243 case 'boolean' : da[c.name] = false; break;
13244 default : da[c.name] = ""; break;
13248 return new this.recordType(Roo.apply(da, d));
13254 * Ext JS Library 1.1.1
13255 * Copyright(c) 2006-2007, Ext JS, LLC.
13257 * Originally Released Under LGPL - original licence link has changed is not relivant.
13260 * <script type="text/javascript">
13264 * @class Roo.data.DataProxy
13265 * @extends Roo.data.Observable
13266 * This class is an abstract base class for implementations which provide retrieval of
13267 * unformatted data objects.<br>
13269 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13270 * (of the appropriate type which knows how to parse the data object) to provide a block of
13271 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13273 * Custom implementations must implement the load method as described in
13274 * {@link Roo.data.HttpProxy#load}.
13276 Roo.data.DataProxy = function(){
13279 * @event beforeload
13280 * Fires before a network request is made to retrieve a data object.
13281 * @param {Object} This DataProxy object.
13282 * @param {Object} params The params parameter to the load function.
13287 * Fires before the load method's callback is called.
13288 * @param {Object} This DataProxy object.
13289 * @param {Object} o The data object.
13290 * @param {Object} arg The callback argument object passed to the load function.
13294 * @event loadexception
13295 * Fires if an Exception occurs during data retrieval.
13296 * @param {Object} This DataProxy object.
13297 * @param {Object} o The data object.
13298 * @param {Object} arg The callback argument object passed to the load function.
13299 * @param {Object} e The Exception.
13301 loadexception : true
13303 Roo.data.DataProxy.superclass.constructor.call(this);
13306 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13309 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13313 * Ext JS Library 1.1.1
13314 * Copyright(c) 2006-2007, Ext JS, LLC.
13316 * Originally Released Under LGPL - original licence link has changed is not relivant.
13319 * <script type="text/javascript">
13322 * @class Roo.data.MemoryProxy
13323 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13324 * to the Reader when its load method is called.
13326 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13328 Roo.data.MemoryProxy = function(data){
13332 Roo.data.MemoryProxy.superclass.constructor.call(this);
13336 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13339 * Load data from the requested source (in this case an in-memory
13340 * data object passed to the constructor), read the data object into
13341 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13342 * process that block using the passed callback.
13343 * @param {Object} params This parameter is not used by the MemoryProxy class.
13344 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13345 * object into a block of Roo.data.Records.
13346 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13347 * The function must be passed <ul>
13348 * <li>The Record block object</li>
13349 * <li>The "arg" argument from the load function</li>
13350 * <li>A boolean success indicator</li>
13352 * @param {Object} scope The scope in which to call the callback
13353 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13355 load : function(params, reader, callback, scope, arg){
13356 params = params || {};
13359 result = reader.readRecords(params.data ? params.data :this.data);
13361 this.fireEvent("loadexception", this, arg, null, e);
13362 callback.call(scope, null, arg, false);
13365 callback.call(scope, result, arg, true);
13369 update : function(params, records){
13374 * Ext JS Library 1.1.1
13375 * Copyright(c) 2006-2007, Ext JS, LLC.
13377 * Originally Released Under LGPL - original licence link has changed is not relivant.
13380 * <script type="text/javascript">
13383 * @class Roo.data.HttpProxy
13384 * @extends Roo.data.DataProxy
13385 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13386 * configured to reference a certain URL.<br><br>
13388 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13389 * from which the running page was served.<br><br>
13391 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13393 * Be aware that to enable the browser to parse an XML document, the server must set
13394 * the Content-Type header in the HTTP response to "text/xml".
13396 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13397 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
13398 * will be used to make the request.
13400 Roo.data.HttpProxy = function(conn){
13401 Roo.data.HttpProxy.superclass.constructor.call(this);
13402 // is conn a conn config or a real conn?
13404 this.useAjax = !conn || !conn.events;
13408 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13409 // thse are take from connection...
13412 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13415 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13416 * extra parameters to each request made by this object. (defaults to undefined)
13419 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13420 * to each request made by this object. (defaults to undefined)
13423 * @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)
13426 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13429 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13435 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13439 * Return the {@link Roo.data.Connection} object being used by this Proxy.
13440 * @return {Connection} The Connection object. This object may be used to subscribe to events on
13441 * a finer-grained basis than the DataProxy events.
13443 getConnection : function(){
13444 return this.useAjax ? Roo.Ajax : this.conn;
13448 * Load data from the configured {@link Roo.data.Connection}, read the data object into
13449 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13450 * process that block using the passed callback.
13451 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13452 * for the request to the remote server.
13453 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13454 * object into a block of Roo.data.Records.
13455 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13456 * The function must be passed <ul>
13457 * <li>The Record block object</li>
13458 * <li>The "arg" argument from the load function</li>
13459 * <li>A boolean success indicator</li>
13461 * @param {Object} scope The scope in which to call the callback
13462 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13464 load : function(params, reader, callback, scope, arg){
13465 if(this.fireEvent("beforeload", this, params) !== false){
13467 params : params || {},
13469 callback : callback,
13474 callback : this.loadResponse,
13478 Roo.applyIf(o, this.conn);
13479 if(this.activeRequest){
13480 Roo.Ajax.abort(this.activeRequest);
13482 this.activeRequest = Roo.Ajax.request(o);
13484 this.conn.request(o);
13487 callback.call(scope||this, null, arg, false);
13492 loadResponse : function(o, success, response){
13493 delete this.activeRequest;
13495 this.fireEvent("loadexception", this, o, response);
13496 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13501 result = o.reader.read(response);
13503 this.fireEvent("loadexception", this, o, response, e);
13504 o.request.callback.call(o.request.scope, null, o.request.arg, false);
13508 this.fireEvent("load", this, o, o.request.arg);
13509 o.request.callback.call(o.request.scope, result, o.request.arg, true);
13513 update : function(dataSet){
13518 updateResponse : function(dataSet){
13523 * Ext JS Library 1.1.1
13524 * Copyright(c) 2006-2007, Ext JS, LLC.
13526 * Originally Released Under LGPL - original licence link has changed is not relivant.
13529 * <script type="text/javascript">
13533 * @class Roo.data.ScriptTagProxy
13534 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13535 * other than the originating domain of the running page.<br><br>
13537 * <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
13538 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13540 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13541 * source code that is used as the source inside a <script> tag.<br><br>
13543 * In order for the browser to process the returned data, the server must wrap the data object
13544 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13545 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13546 * depending on whether the callback name was passed:
13549 boolean scriptTag = false;
13550 String cb = request.getParameter("callback");
13553 response.setContentType("text/javascript");
13555 response.setContentType("application/x-json");
13557 Writer out = response.getWriter();
13559 out.write(cb + "(");
13561 out.print(dataBlock.toJsonString());
13568 * @param {Object} config A configuration object.
13570 Roo.data.ScriptTagProxy = function(config){
13571 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13572 Roo.apply(this, config);
13573 this.head = document.getElementsByTagName("head")[0];
13576 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13578 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13580 * @cfg {String} url The URL from which to request the data object.
13583 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13587 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13588 * the server the name of the callback function set up by the load call to process the returned data object.
13589 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13590 * javascript output which calls this named function passing the data object as its only parameter.
13592 callbackParam : "callback",
13594 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13595 * name to the request.
13600 * Load data from the configured URL, read the data object into
13601 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13602 * process that block using the passed callback.
13603 * @param {Object} params An object containing properties which are to be used as HTTP parameters
13604 * for the request to the remote server.
13605 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13606 * object into a block of Roo.data.Records.
13607 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13608 * The function must be passed <ul>
13609 * <li>The Record block object</li>
13610 * <li>The "arg" argument from the load function</li>
13611 * <li>A boolean success indicator</li>
13613 * @param {Object} scope The scope in which to call the callback
13614 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13616 load : function(params, reader, callback, scope, arg){
13617 if(this.fireEvent("beforeload", this, params) !== false){
13619 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13621 var url = this.url;
13622 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13624 url += "&_dc=" + (new Date().getTime());
13626 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13629 cb : "stcCallback"+transId,
13630 scriptId : "stcScript"+transId,
13634 callback : callback,
13640 window[trans.cb] = function(o){
13641 conn.handleResponse(o, trans);
13644 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13646 if(this.autoAbort !== false){
13650 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13652 var script = document.createElement("script");
13653 script.setAttribute("src", url);
13654 script.setAttribute("type", "text/javascript");
13655 script.setAttribute("id", trans.scriptId);
13656 this.head.appendChild(script);
13658 this.trans = trans;
13660 callback.call(scope||this, null, arg, false);
13665 isLoading : function(){
13666 return this.trans ? true : false;
13670 * Abort the current server request.
13672 abort : function(){
13673 if(this.isLoading()){
13674 this.destroyTrans(this.trans);
13679 destroyTrans : function(trans, isLoaded){
13680 this.head.removeChild(document.getElementById(trans.scriptId));
13681 clearTimeout(trans.timeoutId);
13683 window[trans.cb] = undefined;
13685 delete window[trans.cb];
13688 // if hasn't been loaded, wait for load to remove it to prevent script error
13689 window[trans.cb] = function(){
13690 window[trans.cb] = undefined;
13692 delete window[trans.cb];
13699 handleResponse : function(o, trans){
13700 this.trans = false;
13701 this.destroyTrans(trans, true);
13704 result = trans.reader.readRecords(o);
13706 this.fireEvent("loadexception", this, o, trans.arg, e);
13707 trans.callback.call(trans.scope||window, null, trans.arg, false);
13710 this.fireEvent("load", this, o, trans.arg);
13711 trans.callback.call(trans.scope||window, result, trans.arg, true);
13715 handleFailure : function(trans){
13716 this.trans = false;
13717 this.destroyTrans(trans, false);
13718 this.fireEvent("loadexception", this, null, trans.arg);
13719 trans.callback.call(trans.scope||window, null, trans.arg, false);
13723 * Ext JS Library 1.1.1
13724 * Copyright(c) 2006-2007, Ext JS, LLC.
13726 * Originally Released Under LGPL - original licence link has changed is not relivant.
13729 * <script type="text/javascript">
13733 * @class Roo.data.JsonReader
13734 * @extends Roo.data.DataReader
13735 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13736 * based on mappings in a provided Roo.data.Record constructor.
13738 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13739 * in the reply previously.
13744 var RecordDef = Roo.data.Record.create([
13745 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13746 {name: 'occupation'} // This field will use "occupation" as the mapping.
13748 var myReader = new Roo.data.JsonReader({
13749 totalProperty: "results", // The property which contains the total dataset size (optional)
13750 root: "rows", // The property which contains an Array of row objects
13751 id: "id" // The property within each row object that provides an ID for the record (optional)
13755 * This would consume a JSON file like this:
13757 { 'results': 2, 'rows': [
13758 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13759 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13762 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13763 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13764 * paged from the remote server.
13765 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13766 * @cfg {String} root name of the property which contains the Array of row objects.
13767 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13768 * @cfg {Array} fields Array of field definition objects
13770 * Create a new JsonReader
13771 * @param {Object} meta Metadata configuration options
13772 * @param {Object} recordType Either an Array of field definition objects,
13773 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13775 Roo.data.JsonReader = function(meta, recordType){
13778 // set some defaults:
13779 Roo.applyIf(meta, {
13780 totalProperty: 'total',
13781 successProperty : 'success',
13786 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13788 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13790 readerType : 'Json',
13793 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13794 * Used by Store query builder to append _requestMeta to params.
13797 metaFromRemote : false,
13799 * This method is only used by a DataProxy which has retrieved data from a remote server.
13800 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13801 * @return {Object} data A data block which is used by an Roo.data.Store object as
13802 * a cache of Roo.data.Records.
13804 read : function(response){
13805 var json = response.responseText;
13807 var o = /* eval:var:o */ eval("("+json+")");
13809 throw {message: "JsonReader.read: Json object not found"};
13815 this.metaFromRemote = true;
13816 this.meta = o.metaData;
13817 this.recordType = Roo.data.Record.create(o.metaData.fields);
13818 this.onMetaChange(this.meta, this.recordType, o);
13820 return this.readRecords(o);
13823 // private function a store will implement
13824 onMetaChange : function(meta, recordType, o){
13831 simpleAccess: function(obj, subsc) {
13838 getJsonAccessor: function(){
13840 return function(expr) {
13842 return(re.test(expr))
13843 ? new Function("obj", "return obj." + expr)
13848 return Roo.emptyFn;
13853 * Create a data block containing Roo.data.Records from an XML document.
13854 * @param {Object} o An object which contains an Array of row objects in the property specified
13855 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13856 * which contains the total size of the dataset.
13857 * @return {Object} data A data block which is used by an Roo.data.Store object as
13858 * a cache of Roo.data.Records.
13860 readRecords : function(o){
13862 * After any data loads, the raw JSON data is available for further custom processing.
13866 var s = this.meta, Record = this.recordType,
13867 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13869 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13871 if(s.totalProperty) {
13872 this.getTotal = this.getJsonAccessor(s.totalProperty);
13874 if(s.successProperty) {
13875 this.getSuccess = this.getJsonAccessor(s.successProperty);
13877 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13879 var g = this.getJsonAccessor(s.id);
13880 this.getId = function(rec) {
13882 return (r === undefined || r === "") ? null : r;
13885 this.getId = function(){return null;};
13888 for(var jj = 0; jj < fl; jj++){
13890 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13891 this.ef[jj] = this.getJsonAccessor(map);
13895 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13896 if(s.totalProperty){
13897 var vt = parseInt(this.getTotal(o), 10);
13902 if(s.successProperty){
13903 var vs = this.getSuccess(o);
13904 if(vs === false || vs === 'false'){
13909 for(var i = 0; i < c; i++){
13912 var id = this.getId(n);
13913 for(var j = 0; j < fl; j++){
13915 var v = this.ef[j](n);
13917 Roo.log('missing convert for ' + f.name);
13921 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13923 var record = new Record(values, id);
13925 records[i] = record;
13931 totalRecords : totalRecords
13934 // used when loading children.. @see loadDataFromChildren
13935 toLoadData: function(rec)
13937 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13938 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13939 return { data : data, total : data.length };
13944 * Ext JS Library 1.1.1
13945 * Copyright(c) 2006-2007, Ext JS, LLC.
13947 * Originally Released Under LGPL - original licence link has changed is not relivant.
13950 * <script type="text/javascript">
13954 * @class Roo.data.ArrayReader
13955 * @extends Roo.data.DataReader
13956 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13957 * Each element of that Array represents a row of data fields. The
13958 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13959 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13963 var RecordDef = Roo.data.Record.create([
13964 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13965 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13967 var myReader = new Roo.data.ArrayReader({
13968 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13972 * This would consume an Array like this:
13974 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13978 * Create a new JsonReader
13979 * @param {Object} meta Metadata configuration options.
13980 * @param {Object|Array} recordType Either an Array of field definition objects
13982 * @cfg {Array} fields Array of field definition objects
13983 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13984 * as specified to {@link Roo.data.Record#create},
13985 * or an {@link Roo.data.Record} object
13988 * created using {@link Roo.data.Record#create}.
13990 Roo.data.ArrayReader = function(meta, recordType)
13992 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13995 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13998 * Create a data block containing Roo.data.Records from an XML document.
13999 * @param {Object} o An Array of row objects which represents the dataset.
14000 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14001 * a cache of Roo.data.Records.
14003 readRecords : function(o)
14005 var sid = this.meta ? this.meta.id : null;
14006 var recordType = this.recordType, fields = recordType.prototype.fields;
14009 for(var i = 0; i < root.length; i++){
14012 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14013 for(var j = 0, jlen = fields.length; j < jlen; j++){
14014 var f = fields.items[j];
14015 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14016 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14018 values[f.name] = v;
14020 var record = new recordType(values, id);
14022 records[records.length] = record;
14026 totalRecords : records.length
14029 // used when loading children.. @see loadDataFromChildren
14030 toLoadData: function(rec)
14032 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14033 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14044 * @class Roo.bootstrap.ComboBox
14045 * @extends Roo.bootstrap.TriggerField
14046 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14047 * @cfg {Boolean} append (true|false) default false
14048 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14049 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14050 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14051 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14052 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14053 * @cfg {Boolean} animate default true
14054 * @cfg {Boolean} emptyResultText only for touch device
14055 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14056 * @cfg {String} emptyTitle default ''
14058 * Create a new ComboBox.
14059 * @param {Object} config Configuration options
14061 Roo.bootstrap.ComboBox = function(config){
14062 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14066 * Fires when the dropdown list is expanded
14067 * @param {Roo.bootstrap.ComboBox} combo This combo box
14072 * Fires when the dropdown list is collapsed
14073 * @param {Roo.bootstrap.ComboBox} combo This combo box
14077 * @event beforeselect
14078 * Fires before a list item is selected. Return false to cancel the selection.
14079 * @param {Roo.bootstrap.ComboBox} combo This combo box
14080 * @param {Roo.data.Record} record The data record returned from the underlying store
14081 * @param {Number} index The index of the selected item in the dropdown list
14083 'beforeselect' : true,
14086 * Fires when a list item is selected
14087 * @param {Roo.bootstrap.ComboBox} combo This combo box
14088 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14089 * @param {Number} index The index of the selected item in the dropdown list
14093 * @event beforequery
14094 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14095 * The event object passed has these properties:
14096 * @param {Roo.bootstrap.ComboBox} combo This combo box
14097 * @param {String} query The query
14098 * @param {Boolean} forceAll true to force "all" query
14099 * @param {Boolean} cancel true to cancel the query
14100 * @param {Object} e The query event object
14102 'beforequery': true,
14105 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14106 * @param {Roo.bootstrap.ComboBox} combo This combo box
14111 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14112 * @param {Roo.bootstrap.ComboBox} combo This combo box
14113 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14118 * Fires when the remove value from the combobox array
14119 * @param {Roo.bootstrap.ComboBox} combo This combo box
14123 * @event afterremove
14124 * Fires when the remove value from the combobox array
14125 * @param {Roo.bootstrap.ComboBox} combo This combo box
14127 'afterremove' : true,
14129 * @event specialfilter
14130 * Fires when specialfilter
14131 * @param {Roo.bootstrap.ComboBox} combo This combo box
14133 'specialfilter' : true,
14136 * Fires when tick the element
14137 * @param {Roo.bootstrap.ComboBox} combo This combo box
14141 * @event touchviewdisplay
14142 * Fires when touch view require special display (default is using displayField)
14143 * @param {Roo.bootstrap.ComboBox} combo This combo box
14144 * @param {Object} cfg set html .
14146 'touchviewdisplay' : true
14151 this.tickItems = [];
14153 this.selectedIndex = -1;
14154 if(this.mode == 'local'){
14155 if(config.queryDelay === undefined){
14156 this.queryDelay = 10;
14158 if(config.minChars === undefined){
14164 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14167 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14168 * rendering into an Roo.Editor, defaults to false)
14171 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14172 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14175 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14178 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14179 * the dropdown list (defaults to undefined, with no header element)
14183 * @cfg {String/Roo.Template} tpl The template to use to render the output
14187 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14189 listWidth: undefined,
14191 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14192 * mode = 'remote' or 'text' if mode = 'local')
14194 displayField: undefined,
14197 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14198 * mode = 'remote' or 'value' if mode = 'local').
14199 * Note: use of a valueField requires the user make a selection
14200 * in order for a value to be mapped.
14202 valueField: undefined,
14204 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14209 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14210 * field's data value (defaults to the underlying DOM element's name)
14212 hiddenName: undefined,
14214 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14218 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14220 selectedClass: 'active',
14223 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14227 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14228 * anchor positions (defaults to 'tl-bl')
14230 listAlign: 'tl-bl?',
14232 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14236 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14237 * query specified by the allQuery config option (defaults to 'query')
14239 triggerAction: 'query',
14241 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14242 * (defaults to 4, does not apply if editable = false)
14246 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14247 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14251 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14252 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14256 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14257 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14261 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14262 * when editable = true (defaults to false)
14264 selectOnFocus:false,
14266 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14268 queryParam: 'query',
14270 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14271 * when mode = 'remote' (defaults to 'Loading...')
14273 loadingText: 'Loading...',
14275 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14279 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14283 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14284 * traditional select (defaults to true)
14288 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14292 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14296 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14297 * listWidth has a higher value)
14301 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14302 * allow the user to set arbitrary text into the field (defaults to false)
14304 forceSelection:false,
14306 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14307 * if typeAhead = true (defaults to 250)
14309 typeAheadDelay : 250,
14311 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14312 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14314 valueNotFoundText : undefined,
14316 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14318 blockFocus : false,
14321 * @cfg {Boolean} disableClear Disable showing of clear button.
14323 disableClear : false,
14325 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14327 alwaysQuery : false,
14330 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14335 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14337 invalidClass : "has-warning",
14340 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14342 validClass : "has-success",
14345 * @cfg {Boolean} specialFilter (true|false) special filter default false
14347 specialFilter : false,
14350 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14352 mobileTouchView : true,
14355 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14357 useNativeIOS : false,
14360 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14362 mobile_restrict_height : false,
14364 ios_options : false,
14376 btnPosition : 'right',
14377 triggerList : true,
14378 showToggleBtn : true,
14380 emptyResultText: 'Empty',
14381 triggerText : 'Select',
14384 // element that contains real text value.. (when hidden is used..)
14386 getAutoCreate : function()
14391 * Render classic select for iso
14394 if(Roo.isIOS && this.useNativeIOS){
14395 cfg = this.getAutoCreateNativeIOS();
14403 if(Roo.isTouch && this.mobileTouchView){
14404 cfg = this.getAutoCreateTouchView();
14411 if(!this.tickable){
14412 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14417 * ComboBox with tickable selections
14420 var align = this.labelAlign || this.parentLabelAlign();
14423 cls : 'form-group roo-combobox-tickable' //input-group
14426 var btn_text_select = '';
14427 var btn_text_done = '';
14428 var btn_text_cancel = '';
14430 if (this.btn_text_show) {
14431 btn_text_select = 'Select';
14432 btn_text_done = 'Done';
14433 btn_text_cancel = 'Cancel';
14438 cls : 'tickable-buttons',
14443 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14444 //html : this.triggerText
14445 html: btn_text_select
14451 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14453 html: btn_text_done
14459 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14461 html: btn_text_cancel
14467 buttons.cn.unshift({
14469 cls: 'roo-select2-search-field-input'
14475 Roo.each(buttons.cn, function(c){
14477 c.cls += ' btn-' + _this.size;
14480 if (_this.disabled) {
14487 style : 'display: contents',
14492 cls: 'form-hidden-field'
14496 cls: 'roo-select2-choices',
14500 cls: 'roo-select2-search-field',
14511 cls: 'roo-select2-container input-group roo-select2-container-multi',
14517 // cls: 'typeahead typeahead-long dropdown-menu',
14518 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
14523 if(this.hasFeedback && !this.allowBlank){
14527 cls: 'glyphicon form-control-feedback'
14530 combobox.cn.push(feedback);
14535 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14536 tooltip : 'This field is required'
14538 if (Roo.bootstrap.version == 4) {
14541 style : 'display:none'
14544 if (align ==='left' && this.fieldLabel.length) {
14546 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14553 cls : 'control-label col-form-label',
14554 html : this.fieldLabel
14566 var labelCfg = cfg.cn[1];
14567 var contentCfg = cfg.cn[2];
14570 if(this.indicatorpos == 'right'){
14576 cls : 'control-label col-form-label',
14580 html : this.fieldLabel
14596 labelCfg = cfg.cn[0];
14597 contentCfg = cfg.cn[1];
14601 if(this.labelWidth > 12){
14602 labelCfg.style = "width: " + this.labelWidth + 'px';
14605 if(this.labelWidth < 13 && this.labelmd == 0){
14606 this.labelmd = this.labelWidth;
14609 if(this.labellg > 0){
14610 labelCfg.cls += ' col-lg-' + this.labellg;
14611 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14614 if(this.labelmd > 0){
14615 labelCfg.cls += ' col-md-' + this.labelmd;
14616 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14619 if(this.labelsm > 0){
14620 labelCfg.cls += ' col-sm-' + this.labelsm;
14621 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14624 if(this.labelxs > 0){
14625 labelCfg.cls += ' col-xs-' + this.labelxs;
14626 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14630 } else if ( this.fieldLabel.length) {
14631 // Roo.log(" label");
14636 //cls : 'input-group-addon',
14637 html : this.fieldLabel
14642 if(this.indicatorpos == 'right'){
14646 //cls : 'input-group-addon',
14647 html : this.fieldLabel
14657 // Roo.log(" no label && no align");
14664 ['xs','sm','md','lg'].map(function(size){
14665 if (settings[size]) {
14666 cfg.cls += ' col-' + size + '-' + settings[size];
14674 _initEventsCalled : false,
14677 initEvents: function()
14679 if (this._initEventsCalled) { // as we call render... prevent looping...
14682 this._initEventsCalled = true;
14685 throw "can not find store for combo";
14688 this.indicator = this.indicatorEl();
14690 this.store = Roo.factory(this.store, Roo.data);
14691 this.store.parent = this;
14693 // if we are building from html. then this element is so complex, that we can not really
14694 // use the rendered HTML.
14695 // so we have to trash and replace the previous code.
14696 if (Roo.XComponent.build_from_html) {
14697 // remove this element....
14698 var e = this.el.dom, k=0;
14699 while (e ) { e = e.previousSibling; ++k;}
14704 this.rendered = false;
14706 this.render(this.parent().getChildContainer(true), k);
14709 if(Roo.isIOS && this.useNativeIOS){
14710 this.initIOSView();
14718 if(Roo.isTouch && this.mobileTouchView){
14719 this.initTouchView();
14724 this.initTickableEvents();
14728 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14730 if(this.hiddenName){
14732 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14734 this.hiddenField.dom.value =
14735 this.hiddenValue !== undefined ? this.hiddenValue :
14736 this.value !== undefined ? this.value : '';
14738 // prevent input submission
14739 this.el.dom.removeAttribute('name');
14740 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14745 // this.el.dom.setAttribute('autocomplete', 'off');
14748 var cls = 'x-combo-list';
14750 //this.list = new Roo.Layer({
14751 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14757 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14758 _this.list.setWidth(lw);
14761 this.list.on('mouseover', this.onViewOver, this);
14762 this.list.on('mousemove', this.onViewMove, this);
14763 this.list.on('scroll', this.onViewScroll, this);
14766 this.list.swallowEvent('mousewheel');
14767 this.assetHeight = 0;
14770 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14771 this.assetHeight += this.header.getHeight();
14774 this.innerList = this.list.createChild({cls:cls+'-inner'});
14775 this.innerList.on('mouseover', this.onViewOver, this);
14776 this.innerList.on('mousemove', this.onViewMove, this);
14777 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14779 if(this.allowBlank && !this.pageSize && !this.disableClear){
14780 this.footer = this.list.createChild({cls:cls+'-ft'});
14781 this.pageTb = new Roo.Toolbar(this.footer);
14785 this.footer = this.list.createChild({cls:cls+'-ft'});
14786 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14787 {pageSize: this.pageSize});
14791 if (this.pageTb && this.allowBlank && !this.disableClear) {
14793 this.pageTb.add(new Roo.Toolbar.Fill(), {
14794 cls: 'x-btn-icon x-btn-clear',
14796 handler: function()
14799 _this.clearValue();
14800 _this.onSelect(false, -1);
14805 this.assetHeight += this.footer.getHeight();
14810 this.tpl = Roo.bootstrap.version == 4 ?
14811 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14812 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14815 this.view = new Roo.View(this.list, this.tpl, {
14816 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14818 //this.view.wrapEl.setDisplayed(false);
14819 this.view.on('click', this.onViewClick, this);
14822 this.store.on('beforeload', this.onBeforeLoad, this);
14823 this.store.on('load', this.onLoad, this);
14824 this.store.on('loadexception', this.onLoadException, this);
14826 if(this.resizable){
14827 this.resizer = new Roo.Resizable(this.list, {
14828 pinned:true, handles:'se'
14830 this.resizer.on('resize', function(r, w, h){
14831 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14832 this.listWidth = w;
14833 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14834 this.restrictHeight();
14836 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14839 if(!this.editable){
14840 this.editable = true;
14841 this.setEditable(false);
14846 if (typeof(this.events.add.listeners) != 'undefined') {
14848 this.addicon = this.wrap.createChild(
14849 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14851 this.addicon.on('click', function(e) {
14852 this.fireEvent('add', this);
14855 if (typeof(this.events.edit.listeners) != 'undefined') {
14857 this.editicon = this.wrap.createChild(
14858 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14859 if (this.addicon) {
14860 this.editicon.setStyle('margin-left', '40px');
14862 this.editicon.on('click', function(e) {
14864 // we fire even if inothing is selected..
14865 this.fireEvent('edit', this, this.lastData );
14871 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14872 "up" : function(e){
14873 this.inKeyMode = true;
14877 "down" : function(e){
14878 if(!this.isExpanded()){
14879 this.onTriggerClick();
14881 this.inKeyMode = true;
14886 "enter" : function(e){
14887 // this.onViewClick();
14891 if(this.fireEvent("specialkey", this, e)){
14892 this.onViewClick(false);
14898 "esc" : function(e){
14902 "tab" : function(e){
14905 if(this.fireEvent("specialkey", this, e)){
14906 this.onViewClick(false);
14914 doRelay : function(foo, bar, hname){
14915 if(hname == 'down' || this.scope.isExpanded()){
14916 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14925 this.queryDelay = Math.max(this.queryDelay || 10,
14926 this.mode == 'local' ? 10 : 250);
14929 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14931 if(this.typeAhead){
14932 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14934 if(this.editable !== false){
14935 this.inputEl().on("keyup", this.onKeyUp, this);
14937 if(this.forceSelection){
14938 this.inputEl().on('blur', this.doForce, this);
14942 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14943 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14947 initTickableEvents: function()
14951 if(this.hiddenName){
14953 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14955 this.hiddenField.dom.value =
14956 this.hiddenValue !== undefined ? this.hiddenValue :
14957 this.value !== undefined ? this.value : '';
14959 // prevent input submission
14960 this.el.dom.removeAttribute('name');
14961 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14966 // this.list = this.el.select('ul.dropdown-menu',true).first();
14968 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14969 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14970 if(this.triggerList){
14971 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14974 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14975 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14977 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14978 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14980 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14981 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14983 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14984 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14985 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14988 this.cancelBtn.hide();
14993 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14994 _this.list.setWidth(lw);
14997 this.list.on('mouseover', this.onViewOver, this);
14998 this.list.on('mousemove', this.onViewMove, this);
15000 this.list.on('scroll', this.onViewScroll, this);
15003 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15004 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15007 this.view = new Roo.View(this.list, this.tpl, {
15012 selectedClass: this.selectedClass
15015 //this.view.wrapEl.setDisplayed(false);
15016 this.view.on('click', this.onViewClick, this);
15020 this.store.on('beforeload', this.onBeforeLoad, this);
15021 this.store.on('load', this.onLoad, this);
15022 this.store.on('loadexception', this.onLoadException, this);
15025 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15026 "up" : function(e){
15027 this.inKeyMode = true;
15031 "down" : function(e){
15032 this.inKeyMode = true;
15036 "enter" : function(e){
15037 if(this.fireEvent("specialkey", this, e)){
15038 this.onViewClick(false);
15044 "esc" : function(e){
15045 this.onTickableFooterButtonClick(e, false, false);
15048 "tab" : function(e){
15049 this.fireEvent("specialkey", this, e);
15051 this.onTickableFooterButtonClick(e, false, false);
15058 doRelay : function(e, fn, key){
15059 if(this.scope.isExpanded()){
15060 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15069 this.queryDelay = Math.max(this.queryDelay || 10,
15070 this.mode == 'local' ? 10 : 250);
15073 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15075 if(this.typeAhead){
15076 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15079 if(this.editable !== false){
15080 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15083 this.indicator = this.indicatorEl();
15085 if(this.indicator){
15086 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15087 this.indicator.hide();
15092 onDestroy : function(){
15094 this.view.setStore(null);
15095 this.view.el.removeAllListeners();
15096 this.view.el.remove();
15097 this.view.purgeListeners();
15100 this.list.dom.innerHTML = '';
15104 this.store.un('beforeload', this.onBeforeLoad, this);
15105 this.store.un('load', this.onLoad, this);
15106 this.store.un('loadexception', this.onLoadException, this);
15108 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15112 fireKey : function(e){
15113 if(e.isNavKeyPress() && !this.list.isVisible()){
15114 this.fireEvent("specialkey", this, e);
15119 onResize: function(w, h){
15120 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15122 // if(typeof w != 'number'){
15123 // // we do not handle it!?!?
15126 // var tw = this.trigger.getWidth();
15127 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15128 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15130 // this.inputEl().setWidth( this.adjustWidth('input', x));
15132 // //this.trigger.setStyle('left', x+'px');
15134 // if(this.list && this.listWidth === undefined){
15135 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15136 // this.list.setWidth(lw);
15137 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15145 * Allow or prevent the user from directly editing the field text. If false is passed,
15146 * the user will only be able to select from the items defined in the dropdown list. This method
15147 * is the runtime equivalent of setting the 'editable' config option at config time.
15148 * @param {Boolean} value True to allow the user to directly edit the field text
15150 setEditable : function(value){
15151 if(value == this.editable){
15154 this.editable = value;
15156 this.inputEl().dom.setAttribute('readOnly', true);
15157 this.inputEl().on('mousedown', this.onTriggerClick, this);
15158 this.inputEl().addClass('x-combo-noedit');
15160 this.inputEl().dom.setAttribute('readOnly', false);
15161 this.inputEl().un('mousedown', this.onTriggerClick, this);
15162 this.inputEl().removeClass('x-combo-noedit');
15168 onBeforeLoad : function(combo,opts){
15169 if(!this.hasFocus){
15173 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15175 this.restrictHeight();
15176 this.selectedIndex = -1;
15180 onLoad : function(){
15182 this.hasQuery = false;
15184 if(!this.hasFocus){
15188 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15189 this.loading.hide();
15192 if(this.store.getCount() > 0){
15195 this.restrictHeight();
15196 if(this.lastQuery == this.allQuery){
15197 if(this.editable && !this.tickable){
15198 this.inputEl().dom.select();
15202 !this.selectByValue(this.value, true) &&
15205 !this.store.lastOptions ||
15206 typeof(this.store.lastOptions.add) == 'undefined' ||
15207 this.store.lastOptions.add != true
15210 this.select(0, true);
15213 if(this.autoFocus){
15216 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15217 this.taTask.delay(this.typeAheadDelay);
15221 this.onEmptyResults();
15227 onLoadException : function()
15229 this.hasQuery = false;
15231 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15232 this.loading.hide();
15235 if(this.tickable && this.editable){
15240 // only causes errors at present
15241 //Roo.log(this.store.reader.jsonData);
15242 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15244 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15250 onTypeAhead : function(){
15251 if(this.store.getCount() > 0){
15252 var r = this.store.getAt(0);
15253 var newValue = r.data[this.displayField];
15254 var len = newValue.length;
15255 var selStart = this.getRawValue().length;
15257 if(selStart != len){
15258 this.setRawValue(newValue);
15259 this.selectText(selStart, newValue.length);
15265 onSelect : function(record, index){
15267 if(this.fireEvent('beforeselect', this, record, index) !== false){
15269 this.setFromData(index > -1 ? record.data : false);
15272 this.fireEvent('select', this, record, index);
15277 * Returns the currently selected field value or empty string if no value is set.
15278 * @return {String} value The selected value
15280 getValue : function()
15282 if(Roo.isIOS && this.useNativeIOS){
15283 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15287 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15290 if(this.valueField){
15291 return typeof this.value != 'undefined' ? this.value : '';
15293 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15297 getRawValue : function()
15299 if(Roo.isIOS && this.useNativeIOS){
15300 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15303 var v = this.inputEl().getValue();
15309 * Clears any text/value currently set in the field
15311 clearValue : function(){
15313 if(this.hiddenField){
15314 this.hiddenField.dom.value = '';
15317 this.setRawValue('');
15318 this.lastSelectionText = '';
15319 this.lastData = false;
15321 var close = this.closeTriggerEl();
15332 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15333 * will be displayed in the field. If the value does not match the data value of an existing item,
15334 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15335 * Otherwise the field will be blank (although the value will still be set).
15336 * @param {String} value The value to match
15338 setValue : function(v)
15340 if(Roo.isIOS && this.useNativeIOS){
15341 this.setIOSValue(v);
15351 if(this.valueField){
15352 var r = this.findRecord(this.valueField, v);
15354 text = r.data[this.displayField];
15355 }else if(this.valueNotFoundText !== undefined){
15356 text = this.valueNotFoundText;
15359 this.lastSelectionText = text;
15360 if(this.hiddenField){
15361 this.hiddenField.dom.value = v;
15363 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15366 var close = this.closeTriggerEl();
15369 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15375 * @property {Object} the last set data for the element
15380 * Sets the value of the field based on a object which is related to the record format for the store.
15381 * @param {Object} value the value to set as. or false on reset?
15383 setFromData : function(o){
15390 var dv = ''; // display value
15391 var vv = ''; // value value..
15393 if (this.displayField) {
15394 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15396 // this is an error condition!!!
15397 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15400 if(this.valueField){
15401 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15404 var close = this.closeTriggerEl();
15407 if(dv.length || vv * 1 > 0){
15409 this.blockFocus=true;
15415 if(this.hiddenField){
15416 this.hiddenField.dom.value = vv;
15418 this.lastSelectionText = dv;
15419 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15423 // no hidden field.. - we store the value in 'value', but still display
15424 // display field!!!!
15425 this.lastSelectionText = dv;
15426 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15433 reset : function(){
15434 // overridden so that last data is reset..
15441 this.setValue(this.originalValue);
15442 //this.clearInvalid();
15443 this.lastData = false;
15445 this.view.clearSelections();
15451 findRecord : function(prop, value){
15453 if(this.store.getCount() > 0){
15454 this.store.each(function(r){
15455 if(r.data[prop] == value){
15465 getName: function()
15467 // returns hidden if it's set..
15468 if (!this.rendered) {return ''};
15469 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
15473 onViewMove : function(e, t){
15474 this.inKeyMode = false;
15478 onViewOver : function(e, t){
15479 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15482 var item = this.view.findItemFromChild(t);
15485 var index = this.view.indexOf(item);
15486 this.select(index, false);
15491 onViewClick : function(view, doFocus, el, e)
15493 var index = this.view.getSelectedIndexes()[0];
15495 var r = this.store.getAt(index);
15499 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15506 Roo.each(this.tickItems, function(v,k){
15508 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15510 _this.tickItems.splice(k, 1);
15512 if(typeof(e) == 'undefined' && view == false){
15513 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15525 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15526 this.tickItems.push(r.data);
15529 if(typeof(e) == 'undefined' && view == false){
15530 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15537 this.onSelect(r, index);
15539 if(doFocus !== false && !this.blockFocus){
15540 this.inputEl().focus();
15545 restrictHeight : function(){
15546 //this.innerList.dom.style.height = '';
15547 //var inner = this.innerList.dom;
15548 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15549 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15550 //this.list.beginUpdate();
15551 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15552 this.list.alignTo(this.inputEl(), this.listAlign);
15553 this.list.alignTo(this.inputEl(), this.listAlign);
15554 //this.list.endUpdate();
15558 onEmptyResults : function(){
15560 if(this.tickable && this.editable){
15561 this.hasFocus = false;
15562 this.restrictHeight();
15570 * Returns true if the dropdown list is expanded, else false.
15572 isExpanded : function(){
15573 return this.list.isVisible();
15577 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15578 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15579 * @param {String} value The data value of the item to select
15580 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15581 * selected item if it is not currently in view (defaults to true)
15582 * @return {Boolean} True if the value matched an item in the list, else false
15584 selectByValue : function(v, scrollIntoView){
15585 if(v !== undefined && v !== null){
15586 var r = this.findRecord(this.valueField || this.displayField, v);
15588 this.select(this.store.indexOf(r), scrollIntoView);
15596 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15597 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15598 * @param {Number} index The zero-based index of the list item to select
15599 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15600 * selected item if it is not currently in view (defaults to true)
15602 select : function(index, scrollIntoView){
15603 this.selectedIndex = index;
15604 this.view.select(index);
15605 if(scrollIntoView !== false){
15606 var el = this.view.getNode(index);
15608 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15611 this.list.scrollChildIntoView(el, false);
15617 selectNext : function(){
15618 var ct = this.store.getCount();
15620 if(this.selectedIndex == -1){
15622 }else if(this.selectedIndex < ct-1){
15623 this.select(this.selectedIndex+1);
15629 selectPrev : function(){
15630 var ct = this.store.getCount();
15632 if(this.selectedIndex == -1){
15634 }else if(this.selectedIndex != 0){
15635 this.select(this.selectedIndex-1);
15641 onKeyUp : function(e){
15642 if(this.editable !== false && !e.isSpecialKey()){
15643 this.lastKey = e.getKey();
15644 this.dqTask.delay(this.queryDelay);
15649 validateBlur : function(){
15650 return !this.list || !this.list.isVisible();
15654 initQuery : function(){
15656 var v = this.getRawValue();
15658 if(this.tickable && this.editable){
15659 v = this.tickableInputEl().getValue();
15666 doForce : function(){
15667 if(this.inputEl().dom.value.length > 0){
15668 this.inputEl().dom.value =
15669 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15675 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15676 * query allowing the query action to be canceled if needed.
15677 * @param {String} query The SQL query to execute
15678 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15679 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15680 * saved in the current store (defaults to false)
15682 doQuery : function(q, forceAll){
15684 if(q === undefined || q === null){
15689 forceAll: forceAll,
15693 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15698 forceAll = qe.forceAll;
15699 if(forceAll === true || (q.length >= this.minChars)){
15701 this.hasQuery = true;
15703 if(this.lastQuery != q || this.alwaysQuery){
15704 this.lastQuery = q;
15705 if(this.mode == 'local'){
15706 this.selectedIndex = -1;
15708 this.store.clearFilter();
15711 if(this.specialFilter){
15712 this.fireEvent('specialfilter', this);
15717 this.store.filter(this.displayField, q);
15720 this.store.fireEvent("datachanged", this.store);
15727 this.store.baseParams[this.queryParam] = q;
15729 var options = {params : this.getParams(q)};
15732 options.add = true;
15733 options.params.start = this.page * this.pageSize;
15736 this.store.load(options);
15739 * this code will make the page width larger, at the beginning, the list not align correctly,
15740 * we should expand the list on onLoad
15741 * so command out it
15746 this.selectedIndex = -1;
15751 this.loadNext = false;
15755 getParams : function(q){
15757 //p[this.queryParam] = q;
15761 p.limit = this.pageSize;
15767 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15769 collapse : function(){
15770 if(!this.isExpanded()){
15776 this.hasFocus = false;
15780 this.cancelBtn.hide();
15781 this.trigger.show();
15784 this.tickableInputEl().dom.value = '';
15785 this.tickableInputEl().blur();
15790 Roo.get(document).un('mousedown', this.collapseIf, this);
15791 Roo.get(document).un('mousewheel', this.collapseIf, this);
15792 if (!this.editable) {
15793 Roo.get(document).un('keydown', this.listKeyPress, this);
15795 this.fireEvent('collapse', this);
15801 collapseIf : function(e){
15802 var in_combo = e.within(this.el);
15803 var in_list = e.within(this.list);
15804 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15806 if (in_combo || in_list || is_list) {
15807 //e.stopPropagation();
15812 this.onTickableFooterButtonClick(e, false, false);
15820 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15822 expand : function(){
15824 if(this.isExpanded() || !this.hasFocus){
15828 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15829 this.list.setWidth(lw);
15835 this.restrictHeight();
15839 this.tickItems = Roo.apply([], this.item);
15842 this.cancelBtn.show();
15843 this.trigger.hide();
15846 this.tickableInputEl().focus();
15851 Roo.get(document).on('mousedown', this.collapseIf, this);
15852 Roo.get(document).on('mousewheel', this.collapseIf, this);
15853 if (!this.editable) {
15854 Roo.get(document).on('keydown', this.listKeyPress, this);
15857 this.fireEvent('expand', this);
15861 // Implements the default empty TriggerField.onTriggerClick function
15862 onTriggerClick : function(e)
15864 Roo.log('trigger click');
15866 if(this.disabled || !this.triggerList){
15871 this.loadNext = false;
15873 if(this.isExpanded()){
15875 if (!this.blockFocus) {
15876 this.inputEl().focus();
15880 this.hasFocus = true;
15881 if(this.triggerAction == 'all') {
15882 this.doQuery(this.allQuery, true);
15884 this.doQuery(this.getRawValue());
15886 if (!this.blockFocus) {
15887 this.inputEl().focus();
15892 onTickableTriggerClick : function(e)
15899 this.loadNext = false;
15900 this.hasFocus = true;
15902 if(this.triggerAction == 'all') {
15903 this.doQuery(this.allQuery, true);
15905 this.doQuery(this.getRawValue());
15909 onSearchFieldClick : function(e)
15911 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15912 this.onTickableFooterButtonClick(e, false, false);
15916 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15921 this.loadNext = false;
15922 this.hasFocus = true;
15924 if(this.triggerAction == 'all') {
15925 this.doQuery(this.allQuery, true);
15927 this.doQuery(this.getRawValue());
15931 listKeyPress : function(e)
15933 //Roo.log('listkeypress');
15934 // scroll to first matching element based on key pres..
15935 if (e.isSpecialKey()) {
15938 var k = String.fromCharCode(e.getKey()).toUpperCase();
15941 var csel = this.view.getSelectedNodes();
15942 var cselitem = false;
15944 var ix = this.view.indexOf(csel[0]);
15945 cselitem = this.store.getAt(ix);
15946 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15952 this.store.each(function(v) {
15954 // start at existing selection.
15955 if (cselitem.id == v.id) {
15961 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15962 match = this.store.indexOf(v);
15968 if (match === false) {
15969 return true; // no more action?
15972 this.view.select(match);
15973 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15974 sn.scrollIntoView(sn.dom.parentNode, false);
15977 onViewScroll : function(e, t){
15979 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){
15983 this.hasQuery = true;
15985 this.loading = this.list.select('.loading', true).first();
15987 if(this.loading === null){
15988 this.list.createChild({
15990 cls: 'loading roo-select2-more-results roo-select2-active',
15991 html: 'Loading more results...'
15994 this.loading = this.list.select('.loading', true).first();
15996 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15998 this.loading.hide();
16001 this.loading.show();
16006 this.loadNext = true;
16008 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16013 addItem : function(o)
16015 var dv = ''; // display value
16017 if (this.displayField) {
16018 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16020 // this is an error condition!!!
16021 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16028 var choice = this.choices.createChild({
16030 cls: 'roo-select2-search-choice',
16039 cls: 'roo-select2-search-choice-close fa fa-times',
16044 }, this.searchField);
16046 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16048 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16056 this.inputEl().dom.value = '';
16061 onRemoveItem : function(e, _self, o)
16063 e.preventDefault();
16065 this.lastItem = Roo.apply([], this.item);
16067 var index = this.item.indexOf(o.data) * 1;
16070 Roo.log('not this item?!');
16074 this.item.splice(index, 1);
16079 this.fireEvent('remove', this, e);
16085 syncValue : function()
16087 if(!this.item.length){
16094 Roo.each(this.item, function(i){
16095 if(_this.valueField){
16096 value.push(i[_this.valueField]);
16103 this.value = value.join(',');
16105 if(this.hiddenField){
16106 this.hiddenField.dom.value = this.value;
16109 this.store.fireEvent("datachanged", this.store);
16114 clearItem : function()
16116 if(!this.multiple){
16122 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16130 if(this.tickable && !Roo.isTouch){
16131 this.view.refresh();
16135 inputEl: function ()
16137 if(Roo.isIOS && this.useNativeIOS){
16138 return this.el.select('select.roo-ios-select', true).first();
16141 if(Roo.isTouch && this.mobileTouchView){
16142 return this.el.select('input.form-control',true).first();
16146 return this.searchField;
16149 return this.el.select('input.form-control',true).first();
16152 onTickableFooterButtonClick : function(e, btn, el)
16154 e.preventDefault();
16156 this.lastItem = Roo.apply([], this.item);
16158 if(btn && btn.name == 'cancel'){
16159 this.tickItems = Roo.apply([], this.item);
16168 Roo.each(this.tickItems, function(o){
16176 validate : function()
16178 if(this.getVisibilityEl().hasClass('hidden')){
16182 var v = this.getRawValue();
16185 v = this.getValue();
16188 if(this.disabled || this.allowBlank || v.length){
16193 this.markInvalid();
16197 tickableInputEl : function()
16199 if(!this.tickable || !this.editable){
16200 return this.inputEl();
16203 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16207 getAutoCreateTouchView : function()
16212 cls: 'form-group' //input-group
16218 type : this.inputType,
16219 cls : 'form-control x-combo-noedit',
16220 autocomplete: 'new-password',
16221 placeholder : this.placeholder || '',
16226 input.name = this.name;
16230 input.cls += ' input-' + this.size;
16233 if (this.disabled) {
16234 input.disabled = true;
16245 inputblock.cls += ' input-group';
16247 inputblock.cn.unshift({
16249 cls : 'input-group-addon input-group-prepend input-group-text',
16254 if(this.removable && !this.multiple){
16255 inputblock.cls += ' roo-removable';
16257 inputblock.cn.push({
16260 cls : 'roo-combo-removable-btn close'
16264 if(this.hasFeedback && !this.allowBlank){
16266 inputblock.cls += ' has-feedback';
16268 inputblock.cn.push({
16270 cls: 'glyphicon form-control-feedback'
16277 inputblock.cls += (this.before) ? '' : ' input-group';
16279 inputblock.cn.push({
16281 cls : 'input-group-addon input-group-append input-group-text',
16287 var ibwrap = inputblock;
16292 cls: 'roo-select2-choices',
16296 cls: 'roo-select2-search-field',
16309 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16314 cls: 'form-hidden-field'
16320 if(!this.multiple && this.showToggleBtn){
16326 if (this.caret != false) {
16329 cls: 'fa fa-' + this.caret
16336 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16338 Roo.bootstrap.version == 3 ? caret : '',
16341 cls: 'combobox-clear',
16355 combobox.cls += ' roo-select2-container-multi';
16358 var align = this.labelAlign || this.parentLabelAlign();
16360 if (align ==='left' && this.fieldLabel.length) {
16365 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16366 tooltip : 'This field is required'
16370 cls : 'control-label col-form-label',
16371 html : this.fieldLabel
16382 var labelCfg = cfg.cn[1];
16383 var contentCfg = cfg.cn[2];
16386 if(this.indicatorpos == 'right'){
16391 cls : 'control-label col-form-label',
16395 html : this.fieldLabel
16399 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16400 tooltip : 'This field is required'
16413 labelCfg = cfg.cn[0];
16414 contentCfg = cfg.cn[1];
16419 if(this.labelWidth > 12){
16420 labelCfg.style = "width: " + this.labelWidth + 'px';
16423 if(this.labelWidth < 13 && this.labelmd == 0){
16424 this.labelmd = this.labelWidth;
16427 if(this.labellg > 0){
16428 labelCfg.cls += ' col-lg-' + this.labellg;
16429 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16432 if(this.labelmd > 0){
16433 labelCfg.cls += ' col-md-' + this.labelmd;
16434 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16437 if(this.labelsm > 0){
16438 labelCfg.cls += ' col-sm-' + this.labelsm;
16439 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16442 if(this.labelxs > 0){
16443 labelCfg.cls += ' col-xs-' + this.labelxs;
16444 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16448 } else if ( this.fieldLabel.length) {
16452 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16453 tooltip : 'This field is required'
16457 cls : 'control-label',
16458 html : this.fieldLabel
16469 if(this.indicatorpos == 'right'){
16473 cls : 'control-label',
16474 html : this.fieldLabel,
16478 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16479 tooltip : 'This field is required'
16496 var settings = this;
16498 ['xs','sm','md','lg'].map(function(size){
16499 if (settings[size]) {
16500 cfg.cls += ' col-' + size + '-' + settings[size];
16507 initTouchView : function()
16509 this.renderTouchView();
16511 this.touchViewEl.on('scroll', function(){
16512 this.el.dom.scrollTop = 0;
16515 this.originalValue = this.getValue();
16517 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16519 this.inputEl().on("click", this.showTouchView, this);
16520 if (this.triggerEl) {
16521 this.triggerEl.on("click", this.showTouchView, this);
16525 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16526 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16528 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16530 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16531 this.store.on('load', this.onTouchViewLoad, this);
16532 this.store.on('loadexception', this.onTouchViewLoadException, this);
16534 if(this.hiddenName){
16536 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16538 this.hiddenField.dom.value =
16539 this.hiddenValue !== undefined ? this.hiddenValue :
16540 this.value !== undefined ? this.value : '';
16542 this.el.dom.removeAttribute('name');
16543 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16547 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16548 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16551 if(this.removable && !this.multiple){
16552 var close = this.closeTriggerEl();
16554 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16555 close.on('click', this.removeBtnClick, this, close);
16559 * fix the bug in Safari iOS8
16561 this.inputEl().on("focus", function(e){
16562 document.activeElement.blur();
16565 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16572 renderTouchView : function()
16574 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16575 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16577 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16578 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16580 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16581 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16582 this.touchViewBodyEl.setStyle('overflow', 'auto');
16584 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16585 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16587 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16588 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16592 showTouchView : function()
16598 this.touchViewHeaderEl.hide();
16600 if(this.modalTitle.length){
16601 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16602 this.touchViewHeaderEl.show();
16605 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16606 this.touchViewEl.show();
16608 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16610 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16611 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16613 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16615 if(this.modalTitle.length){
16616 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16619 this.touchViewBodyEl.setHeight(bodyHeight);
16623 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16625 this.touchViewEl.addClass('in');
16628 if(this._touchViewMask){
16629 Roo.get(document.body).addClass("x-body-masked");
16630 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16631 this._touchViewMask.setStyle('z-index', 10000);
16632 this._touchViewMask.addClass('show');
16635 this.doTouchViewQuery();
16639 hideTouchView : function()
16641 this.touchViewEl.removeClass('in');
16645 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16647 this.touchViewEl.setStyle('display', 'none');
16650 if(this._touchViewMask){
16651 this._touchViewMask.removeClass('show');
16652 Roo.get(document.body).removeClass("x-body-masked");
16656 setTouchViewValue : function()
16663 Roo.each(this.tickItems, function(o){
16668 this.hideTouchView();
16671 doTouchViewQuery : function()
16680 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16684 if(!this.alwaysQuery || this.mode == 'local'){
16685 this.onTouchViewLoad();
16692 onTouchViewBeforeLoad : function(combo,opts)
16698 onTouchViewLoad : function()
16700 if(this.store.getCount() < 1){
16701 this.onTouchViewEmptyResults();
16705 this.clearTouchView();
16707 var rawValue = this.getRawValue();
16709 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16711 this.tickItems = [];
16713 this.store.data.each(function(d, rowIndex){
16714 var row = this.touchViewListGroup.createChild(template);
16716 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16717 row.addClass(d.data.cls);
16720 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16723 html : d.data[this.displayField]
16726 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16727 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16730 row.removeClass('selected');
16731 if(!this.multiple && this.valueField &&
16732 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16735 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16736 row.addClass('selected');
16739 if(this.multiple && this.valueField &&
16740 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16744 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16745 this.tickItems.push(d.data);
16748 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16752 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16754 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16756 if(this.modalTitle.length){
16757 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16760 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16762 if(this.mobile_restrict_height && listHeight < bodyHeight){
16763 this.touchViewBodyEl.setHeight(listHeight);
16768 if(firstChecked && listHeight > bodyHeight){
16769 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16774 onTouchViewLoadException : function()
16776 this.hideTouchView();
16779 onTouchViewEmptyResults : function()
16781 this.clearTouchView();
16783 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16785 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16789 clearTouchView : function()
16791 this.touchViewListGroup.dom.innerHTML = '';
16794 onTouchViewClick : function(e, el, o)
16796 e.preventDefault();
16799 var rowIndex = o.rowIndex;
16801 var r = this.store.getAt(rowIndex);
16803 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16805 if(!this.multiple){
16806 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16807 c.dom.removeAttribute('checked');
16810 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16812 this.setFromData(r.data);
16814 var close = this.closeTriggerEl();
16820 this.hideTouchView();
16822 this.fireEvent('select', this, r, rowIndex);
16827 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16828 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16829 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16833 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16834 this.addItem(r.data);
16835 this.tickItems.push(r.data);
16839 getAutoCreateNativeIOS : function()
16842 cls: 'form-group' //input-group,
16847 cls : 'roo-ios-select'
16851 combobox.name = this.name;
16854 if (this.disabled) {
16855 combobox.disabled = true;
16858 var settings = this;
16860 ['xs','sm','md','lg'].map(function(size){
16861 if (settings[size]) {
16862 cfg.cls += ' col-' + size + '-' + settings[size];
16872 initIOSView : function()
16874 this.store.on('load', this.onIOSViewLoad, this);
16879 onIOSViewLoad : function()
16881 if(this.store.getCount() < 1){
16885 this.clearIOSView();
16887 if(this.allowBlank) {
16889 var default_text = '-- SELECT --';
16891 if(this.placeholder.length){
16892 default_text = this.placeholder;
16895 if(this.emptyTitle.length){
16896 default_text += ' - ' + this.emptyTitle + ' -';
16899 var opt = this.inputEl().createChild({
16902 html : default_text
16906 o[this.valueField] = 0;
16907 o[this.displayField] = default_text;
16909 this.ios_options.push({
16916 this.store.data.each(function(d, rowIndex){
16920 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16921 html = d.data[this.displayField];
16926 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16927 value = d.data[this.valueField];
16936 if(this.value == d.data[this.valueField]){
16937 option['selected'] = true;
16940 var opt = this.inputEl().createChild(option);
16942 this.ios_options.push({
16949 this.inputEl().on('change', function(){
16950 this.fireEvent('select', this);
16955 clearIOSView: function()
16957 this.inputEl().dom.innerHTML = '';
16959 this.ios_options = [];
16962 setIOSValue: function(v)
16966 if(!this.ios_options){
16970 Roo.each(this.ios_options, function(opts){
16972 opts.el.dom.removeAttribute('selected');
16974 if(opts.data[this.valueField] != v){
16978 opts.el.dom.setAttribute('selected', true);
16984 * @cfg {Boolean} grow
16988 * @cfg {Number} growMin
16992 * @cfg {Number} growMax
17001 Roo.apply(Roo.bootstrap.ComboBox, {
17005 cls: 'modal-header',
17027 cls: 'list-group-item',
17031 cls: 'roo-combobox-list-group-item-value'
17035 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17049 listItemCheckbox : {
17051 cls: 'list-group-item',
17055 cls: 'roo-combobox-list-group-item-value'
17059 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17075 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17080 cls: 'modal-footer',
17088 cls: 'col-xs-6 text-left',
17091 cls: 'btn btn-danger roo-touch-view-cancel',
17097 cls: 'col-xs-6 text-right',
17100 cls: 'btn btn-success roo-touch-view-ok',
17111 Roo.apply(Roo.bootstrap.ComboBox, {
17113 touchViewTemplate : {
17115 cls: 'modal fade roo-combobox-touch-view',
17119 cls: 'modal-dialog',
17120 style : 'position:fixed', // we have to fix position....
17124 cls: 'modal-content',
17126 Roo.bootstrap.ComboBox.header,
17127 Roo.bootstrap.ComboBox.body,
17128 Roo.bootstrap.ComboBox.footer
17137 * Ext JS Library 1.1.1
17138 * Copyright(c) 2006-2007, Ext JS, LLC.
17140 * Originally Released Under LGPL - original licence link has changed is not relivant.
17143 * <script type="text/javascript">
17148 * @extends Roo.util.Observable
17149 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17150 * This class also supports single and multi selection modes. <br>
17151 * Create a data model bound view:
17153 var store = new Roo.data.Store(...);
17155 var view = new Roo.View({
17157 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17159 singleSelect: true,
17160 selectedClass: "ydataview-selected",
17164 // listen for node click?
17165 view.on("click", function(vw, index, node, e){
17166 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17170 dataModel.load("foobar.xml");
17172 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17174 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17175 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17177 * Note: old style constructor is still suported (container, template, config)
17180 * Create a new View
17181 * @param {Object} config The config object
17184 Roo.View = function(config, depreciated_tpl, depreciated_config){
17186 this.parent = false;
17188 if (typeof(depreciated_tpl) == 'undefined') {
17189 // new way.. - universal constructor.
17190 Roo.apply(this, config);
17191 this.el = Roo.get(this.el);
17194 this.el = Roo.get(config);
17195 this.tpl = depreciated_tpl;
17196 Roo.apply(this, depreciated_config);
17198 this.wrapEl = this.el.wrap().wrap();
17199 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17202 if(typeof(this.tpl) == "string"){
17203 this.tpl = new Roo.Template(this.tpl);
17205 // support xtype ctors..
17206 this.tpl = new Roo.factory(this.tpl, Roo);
17210 this.tpl.compile();
17215 * @event beforeclick
17216 * Fires before a click is processed. Returns false to cancel the default action.
17217 * @param {Roo.View} this
17218 * @param {Number} index The index of the target node
17219 * @param {HTMLElement} node The target node
17220 * @param {Roo.EventObject} e The raw event object
17222 "beforeclick" : true,
17225 * Fires when a template node is clicked.
17226 * @param {Roo.View} this
17227 * @param {Number} index The index of the target node
17228 * @param {HTMLElement} node The target node
17229 * @param {Roo.EventObject} e The raw event object
17234 * Fires when a template node is double clicked.
17235 * @param {Roo.View} this
17236 * @param {Number} index The index of the target node
17237 * @param {HTMLElement} node The target node
17238 * @param {Roo.EventObject} e The raw event object
17242 * @event contextmenu
17243 * Fires when a template node is right clicked.
17244 * @param {Roo.View} this
17245 * @param {Number} index The index of the target node
17246 * @param {HTMLElement} node The target node
17247 * @param {Roo.EventObject} e The raw event object
17249 "contextmenu" : true,
17251 * @event selectionchange
17252 * Fires when the selected nodes change.
17253 * @param {Roo.View} this
17254 * @param {Array} selections Array of the selected nodes
17256 "selectionchange" : true,
17259 * @event beforeselect
17260 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17261 * @param {Roo.View} this
17262 * @param {HTMLElement} node The node to be selected
17263 * @param {Array} selections Array of currently selected nodes
17265 "beforeselect" : true,
17267 * @event preparedata
17268 * Fires on every row to render, to allow you to change the data.
17269 * @param {Roo.View} this
17270 * @param {Object} data to be rendered (change this)
17272 "preparedata" : true
17280 "click": this.onClick,
17281 "dblclick": this.onDblClick,
17282 "contextmenu": this.onContextMenu,
17286 this.selections = [];
17288 this.cmp = new Roo.CompositeElementLite([]);
17290 this.store = Roo.factory(this.store, Roo.data);
17291 this.setStore(this.store, true);
17294 if ( this.footer && this.footer.xtype) {
17296 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17298 this.footer.dataSource = this.store;
17299 this.footer.container = fctr;
17300 this.footer = Roo.factory(this.footer, Roo);
17301 fctr.insertFirst(this.el);
17303 // this is a bit insane - as the paging toolbar seems to detach the el..
17304 // dom.parentNode.parentNode.parentNode
17305 // they get detached?
17309 Roo.View.superclass.constructor.call(this);
17314 Roo.extend(Roo.View, Roo.util.Observable, {
17317 * @cfg {Roo.data.Store} store Data store to load data from.
17322 * @cfg {String|Roo.Element} el The container element.
17327 * @cfg {String|Roo.Template} tpl The template used by this View
17331 * @cfg {String} dataName the named area of the template to use as the data area
17332 * Works with domtemplates roo-name="name"
17336 * @cfg {String} selectedClass The css class to add to selected nodes
17338 selectedClass : "x-view-selected",
17340 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17345 * @cfg {String} text to display on mask (default Loading)
17349 * @cfg {Boolean} multiSelect Allow multiple selection
17351 multiSelect : false,
17353 * @cfg {Boolean} singleSelect Allow single selection
17355 singleSelect: false,
17358 * @cfg {Boolean} toggleSelect - selecting
17360 toggleSelect : false,
17363 * @cfg {Boolean} tickable - selecting
17368 * Returns the element this view is bound to.
17369 * @return {Roo.Element}
17371 getEl : function(){
17372 return this.wrapEl;
17378 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17380 refresh : function(){
17381 //Roo.log('refresh');
17384 // if we are using something like 'domtemplate', then
17385 // the what gets used is:
17386 // t.applySubtemplate(NAME, data, wrapping data..)
17387 // the outer template then get' applied with
17388 // the store 'extra data'
17389 // and the body get's added to the
17390 // roo-name="data" node?
17391 // <span class='roo-tpl-{name}'></span> ?????
17395 this.clearSelections();
17396 this.el.update("");
17398 var records = this.store.getRange();
17399 if(records.length < 1) {
17401 // is this valid?? = should it render a template??
17403 this.el.update(this.emptyText);
17407 if (this.dataName) {
17408 this.el.update(t.apply(this.store.meta)); //????
17409 el = this.el.child('.roo-tpl-' + this.dataName);
17412 for(var i = 0, len = records.length; i < len; i++){
17413 var data = this.prepareData(records[i].data, i, records[i]);
17414 this.fireEvent("preparedata", this, data, i, records[i]);
17416 var d = Roo.apply({}, data);
17419 Roo.apply(d, {'roo-id' : Roo.id()});
17423 Roo.each(this.parent.item, function(item){
17424 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17427 Roo.apply(d, {'roo-data-checked' : 'checked'});
17431 html[html.length] = Roo.util.Format.trim(
17433 t.applySubtemplate(this.dataName, d, this.store.meta) :
17440 el.update(html.join(""));
17441 this.nodes = el.dom.childNodes;
17442 this.updateIndexes(0);
17447 * Function to override to reformat the data that is sent to
17448 * the template for each node.
17449 * DEPRICATED - use the preparedata event handler.
17450 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17451 * a JSON object for an UpdateManager bound view).
17453 prepareData : function(data, index, record)
17455 this.fireEvent("preparedata", this, data, index, record);
17459 onUpdate : function(ds, record){
17460 // Roo.log('on update');
17461 this.clearSelections();
17462 var index = this.store.indexOf(record);
17463 var n = this.nodes[index];
17464 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17465 n.parentNode.removeChild(n);
17466 this.updateIndexes(index, index);
17472 onAdd : function(ds, records, index)
17474 //Roo.log(['on Add', ds, records, index] );
17475 this.clearSelections();
17476 if(this.nodes.length == 0){
17480 var n = this.nodes[index];
17481 for(var i = 0, len = records.length; i < len; i++){
17482 var d = this.prepareData(records[i].data, i, records[i]);
17484 this.tpl.insertBefore(n, d);
17487 this.tpl.append(this.el, d);
17490 this.updateIndexes(index);
17493 onRemove : function(ds, record, index){
17494 // Roo.log('onRemove');
17495 this.clearSelections();
17496 var el = this.dataName ?
17497 this.el.child('.roo-tpl-' + this.dataName) :
17500 el.dom.removeChild(this.nodes[index]);
17501 this.updateIndexes(index);
17505 * Refresh an individual node.
17506 * @param {Number} index
17508 refreshNode : function(index){
17509 this.onUpdate(this.store, this.store.getAt(index));
17512 updateIndexes : function(startIndex, endIndex){
17513 var ns = this.nodes;
17514 startIndex = startIndex || 0;
17515 endIndex = endIndex || ns.length - 1;
17516 for(var i = startIndex; i <= endIndex; i++){
17517 ns[i].nodeIndex = i;
17522 * Changes the data store this view uses and refresh the view.
17523 * @param {Store} store
17525 setStore : function(store, initial){
17526 if(!initial && this.store){
17527 this.store.un("datachanged", this.refresh);
17528 this.store.un("add", this.onAdd);
17529 this.store.un("remove", this.onRemove);
17530 this.store.un("update", this.onUpdate);
17531 this.store.un("clear", this.refresh);
17532 this.store.un("beforeload", this.onBeforeLoad);
17533 this.store.un("load", this.onLoad);
17534 this.store.un("loadexception", this.onLoad);
17538 store.on("datachanged", this.refresh, this);
17539 store.on("add", this.onAdd, this);
17540 store.on("remove", this.onRemove, this);
17541 store.on("update", this.onUpdate, this);
17542 store.on("clear", this.refresh, this);
17543 store.on("beforeload", this.onBeforeLoad, this);
17544 store.on("load", this.onLoad, this);
17545 store.on("loadexception", this.onLoad, this);
17553 * onbeforeLoad - masks the loading area.
17556 onBeforeLoad : function(store,opts)
17558 //Roo.log('onBeforeLoad');
17560 this.el.update("");
17562 this.el.mask(this.mask ? this.mask : "Loading" );
17564 onLoad : function ()
17571 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17572 * @param {HTMLElement} node
17573 * @return {HTMLElement} The template node
17575 findItemFromChild : function(node){
17576 var el = this.dataName ?
17577 this.el.child('.roo-tpl-' + this.dataName,true) :
17580 if(!node || node.parentNode == el){
17583 var p = node.parentNode;
17584 while(p && p != el){
17585 if(p.parentNode == el){
17594 onClick : function(e){
17595 var item = this.findItemFromChild(e.getTarget());
17597 var index = this.indexOf(item);
17598 if(this.onItemClick(item, index, e) !== false){
17599 this.fireEvent("click", this, index, item, e);
17602 this.clearSelections();
17607 onContextMenu : function(e){
17608 var item = this.findItemFromChild(e.getTarget());
17610 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17615 onDblClick : function(e){
17616 var item = this.findItemFromChild(e.getTarget());
17618 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17622 onItemClick : function(item, index, e)
17624 if(this.fireEvent("beforeclick", this, index, item, e) === false){
17627 if (this.toggleSelect) {
17628 var m = this.isSelected(item) ? 'unselect' : 'select';
17631 _t[m](item, true, false);
17634 if(this.multiSelect || this.singleSelect){
17635 if(this.multiSelect && e.shiftKey && this.lastSelection){
17636 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17638 this.select(item, this.multiSelect && e.ctrlKey);
17639 this.lastSelection = item;
17642 if(!this.tickable){
17643 e.preventDefault();
17651 * Get the number of selected nodes.
17654 getSelectionCount : function(){
17655 return this.selections.length;
17659 * Get the currently selected nodes.
17660 * @return {Array} An array of HTMLElements
17662 getSelectedNodes : function(){
17663 return this.selections;
17667 * Get the indexes of the selected nodes.
17670 getSelectedIndexes : function(){
17671 var indexes = [], s = this.selections;
17672 for(var i = 0, len = s.length; i < len; i++){
17673 indexes.push(s[i].nodeIndex);
17679 * Clear all selections
17680 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17682 clearSelections : function(suppressEvent){
17683 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17684 this.cmp.elements = this.selections;
17685 this.cmp.removeClass(this.selectedClass);
17686 this.selections = [];
17687 if(!suppressEvent){
17688 this.fireEvent("selectionchange", this, this.selections);
17694 * Returns true if the passed node is selected
17695 * @param {HTMLElement/Number} node The node or node index
17696 * @return {Boolean}
17698 isSelected : function(node){
17699 var s = this.selections;
17703 node = this.getNode(node);
17704 return s.indexOf(node) !== -1;
17709 * @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
17710 * @param {Boolean} keepExisting (optional) true to keep existing selections
17711 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17713 select : function(nodeInfo, keepExisting, suppressEvent){
17714 if(nodeInfo instanceof Array){
17716 this.clearSelections(true);
17718 for(var i = 0, len = nodeInfo.length; i < len; i++){
17719 this.select(nodeInfo[i], true, true);
17723 var node = this.getNode(nodeInfo);
17724 if(!node || this.isSelected(node)){
17725 return; // already selected.
17728 this.clearSelections(true);
17731 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17732 Roo.fly(node).addClass(this.selectedClass);
17733 this.selections.push(node);
17734 if(!suppressEvent){
17735 this.fireEvent("selectionchange", this, this.selections);
17743 * @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
17744 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17745 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17747 unselect : function(nodeInfo, keepExisting, suppressEvent)
17749 if(nodeInfo instanceof Array){
17750 Roo.each(this.selections, function(s) {
17751 this.unselect(s, nodeInfo);
17755 var node = this.getNode(nodeInfo);
17756 if(!node || !this.isSelected(node)){
17757 //Roo.log("not selected");
17758 return; // not selected.
17762 Roo.each(this.selections, function(s) {
17764 Roo.fly(node).removeClass(this.selectedClass);
17771 this.selections= ns;
17772 this.fireEvent("selectionchange", this, this.selections);
17776 * Gets a template node.
17777 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17778 * @return {HTMLElement} The node or null if it wasn't found
17780 getNode : function(nodeInfo){
17781 if(typeof nodeInfo == "string"){
17782 return document.getElementById(nodeInfo);
17783 }else if(typeof nodeInfo == "number"){
17784 return this.nodes[nodeInfo];
17790 * Gets a range template nodes.
17791 * @param {Number} startIndex
17792 * @param {Number} endIndex
17793 * @return {Array} An array of nodes
17795 getNodes : function(start, end){
17796 var ns = this.nodes;
17797 start = start || 0;
17798 end = typeof end == "undefined" ? ns.length - 1 : end;
17801 for(var i = start; i <= end; i++){
17805 for(var i = start; i >= end; i--){
17813 * Finds the index of the passed node
17814 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17815 * @return {Number} The index of the node or -1
17817 indexOf : function(node){
17818 node = this.getNode(node);
17819 if(typeof node.nodeIndex == "number"){
17820 return node.nodeIndex;
17822 var ns = this.nodes;
17823 for(var i = 0, len = ns.length; i < len; i++){
17834 * based on jquery fullcalendar
17838 Roo.bootstrap = Roo.bootstrap || {};
17840 * @class Roo.bootstrap.Calendar
17841 * @extends Roo.bootstrap.Component
17842 * Bootstrap Calendar class
17843 * @cfg {Boolean} loadMask (true|false) default false
17844 * @cfg {Object} header generate the user specific header of the calendar, default false
17847 * Create a new Container
17848 * @param {Object} config The config object
17853 Roo.bootstrap.Calendar = function(config){
17854 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17858 * Fires when a date is selected
17859 * @param {DatePicker} this
17860 * @param {Date} date The selected date
17864 * @event monthchange
17865 * Fires when the displayed month changes
17866 * @param {DatePicker} this
17867 * @param {Date} date The selected month
17869 'monthchange': true,
17871 * @event evententer
17872 * Fires when mouse over an event
17873 * @param {Calendar} this
17874 * @param {event} Event
17876 'evententer': true,
17878 * @event eventleave
17879 * Fires when the mouse leaves an
17880 * @param {Calendar} this
17883 'eventleave': true,
17885 * @event eventclick
17886 * Fires when the mouse click an
17887 * @param {Calendar} this
17896 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17899 * @cfg {Number} startDay
17900 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17908 getAutoCreate : function(){
17911 var fc_button = function(name, corner, style, content ) {
17912 return Roo.apply({},{
17914 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17916 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17919 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17930 style : 'width:100%',
17937 cls : 'fc-header-left',
17939 fc_button('prev', 'left', 'arrow', '‹' ),
17940 fc_button('next', 'right', 'arrow', '›' ),
17941 { tag: 'span', cls: 'fc-header-space' },
17942 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17950 cls : 'fc-header-center',
17954 cls: 'fc-header-title',
17957 html : 'month / year'
17965 cls : 'fc-header-right',
17967 /* fc_button('month', 'left', '', 'month' ),
17968 fc_button('week', '', '', 'week' ),
17969 fc_button('day', 'right', '', 'day' )
17981 header = this.header;
17984 var cal_heads = function() {
17986 // fixme - handle this.
17988 for (var i =0; i < Date.dayNames.length; i++) {
17989 var d = Date.dayNames[i];
17992 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17993 html : d.substring(0,3)
17997 ret[0].cls += ' fc-first';
17998 ret[6].cls += ' fc-last';
18001 var cal_cell = function(n) {
18004 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18009 cls: 'fc-day-number',
18013 cls: 'fc-day-content',
18017 style: 'position: relative;' // height: 17px;
18029 var cal_rows = function() {
18032 for (var r = 0; r < 6; r++) {
18039 for (var i =0; i < Date.dayNames.length; i++) {
18040 var d = Date.dayNames[i];
18041 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18044 row.cn[0].cls+=' fc-first';
18045 row.cn[0].cn[0].style = 'min-height:90px';
18046 row.cn[6].cls+=' fc-last';
18050 ret[0].cls += ' fc-first';
18051 ret[4].cls += ' fc-prev-last';
18052 ret[5].cls += ' fc-last';
18059 cls: 'fc-border-separate',
18060 style : 'width:100%',
18068 cls : 'fc-first fc-last',
18086 cls : 'fc-content',
18087 style : "position: relative;",
18090 cls : 'fc-view fc-view-month fc-grid',
18091 style : 'position: relative',
18092 unselectable : 'on',
18095 cls : 'fc-event-container',
18096 style : 'position:absolute;z-index:8;top:0;left:0;'
18114 initEvents : function()
18117 throw "can not find store for calendar";
18123 style: "text-align:center",
18127 style: "background-color:white;width:50%;margin:250 auto",
18131 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18142 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18144 var size = this.el.select('.fc-content', true).first().getSize();
18145 this.maskEl.setSize(size.width, size.height);
18146 this.maskEl.enableDisplayMode("block");
18147 if(!this.loadMask){
18148 this.maskEl.hide();
18151 this.store = Roo.factory(this.store, Roo.data);
18152 this.store.on('load', this.onLoad, this);
18153 this.store.on('beforeload', this.onBeforeLoad, this);
18157 this.cells = this.el.select('.fc-day',true);
18158 //Roo.log(this.cells);
18159 this.textNodes = this.el.query('.fc-day-number');
18160 this.cells.addClassOnOver('fc-state-hover');
18162 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18163 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18164 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18165 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18167 this.on('monthchange', this.onMonthChange, this);
18169 this.update(new Date().clearTime());
18172 resize : function() {
18173 var sz = this.el.getSize();
18175 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18176 this.el.select('.fc-day-content div',true).setHeight(34);
18181 showPrevMonth : function(e){
18182 this.update(this.activeDate.add("mo", -1));
18184 showToday : function(e){
18185 this.update(new Date().clearTime());
18188 showNextMonth : function(e){
18189 this.update(this.activeDate.add("mo", 1));
18193 showPrevYear : function(){
18194 this.update(this.activeDate.add("y", -1));
18198 showNextYear : function(){
18199 this.update(this.activeDate.add("y", 1));
18204 update : function(date)
18206 var vd = this.activeDate;
18207 this.activeDate = date;
18208 // if(vd && this.el){
18209 // var t = date.getTime();
18210 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18211 // Roo.log('using add remove');
18213 // this.fireEvent('monthchange', this, date);
18215 // this.cells.removeClass("fc-state-highlight");
18216 // this.cells.each(function(c){
18217 // if(c.dateValue == t){
18218 // c.addClass("fc-state-highlight");
18219 // setTimeout(function(){
18220 // try{c.dom.firstChild.focus();}catch(e){}
18230 var days = date.getDaysInMonth();
18232 var firstOfMonth = date.getFirstDateOfMonth();
18233 var startingPos = firstOfMonth.getDay()-this.startDay;
18235 if(startingPos < this.startDay){
18239 var pm = date.add(Date.MONTH, -1);
18240 var prevStart = pm.getDaysInMonth()-startingPos;
18242 this.cells = this.el.select('.fc-day',true);
18243 this.textNodes = this.el.query('.fc-day-number');
18244 this.cells.addClassOnOver('fc-state-hover');
18246 var cells = this.cells.elements;
18247 var textEls = this.textNodes;
18249 Roo.each(cells, function(cell){
18250 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18253 days += startingPos;
18255 // convert everything to numbers so it's fast
18256 var day = 86400000;
18257 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18260 //Roo.log(prevStart);
18262 var today = new Date().clearTime().getTime();
18263 var sel = date.clearTime().getTime();
18264 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18265 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18266 var ddMatch = this.disabledDatesRE;
18267 var ddText = this.disabledDatesText;
18268 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18269 var ddaysText = this.disabledDaysText;
18270 var format = this.format;
18272 var setCellClass = function(cal, cell){
18276 //Roo.log('set Cell Class');
18278 var t = d.getTime();
18282 cell.dateValue = t;
18284 cell.className += " fc-today";
18285 cell.className += " fc-state-highlight";
18286 cell.title = cal.todayText;
18289 // disable highlight in other month..
18290 //cell.className += " fc-state-highlight";
18295 cell.className = " fc-state-disabled";
18296 cell.title = cal.minText;
18300 cell.className = " fc-state-disabled";
18301 cell.title = cal.maxText;
18305 if(ddays.indexOf(d.getDay()) != -1){
18306 cell.title = ddaysText;
18307 cell.className = " fc-state-disabled";
18310 if(ddMatch && format){
18311 var fvalue = d.dateFormat(format);
18312 if(ddMatch.test(fvalue)){
18313 cell.title = ddText.replace("%0", fvalue);
18314 cell.className = " fc-state-disabled";
18318 if (!cell.initialClassName) {
18319 cell.initialClassName = cell.dom.className;
18322 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18327 for(; i < startingPos; i++) {
18328 textEls[i].innerHTML = (++prevStart);
18329 d.setDate(d.getDate()+1);
18331 cells[i].className = "fc-past fc-other-month";
18332 setCellClass(this, cells[i]);
18337 for(; i < days; i++){
18338 intDay = i - startingPos + 1;
18339 textEls[i].innerHTML = (intDay);
18340 d.setDate(d.getDate()+1);
18342 cells[i].className = ''; // "x-date-active";
18343 setCellClass(this, cells[i]);
18347 for(; i < 42; i++) {
18348 textEls[i].innerHTML = (++extraDays);
18349 d.setDate(d.getDate()+1);
18351 cells[i].className = "fc-future fc-other-month";
18352 setCellClass(this, cells[i]);
18355 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18357 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18359 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18360 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18362 if(totalRows != 6){
18363 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18364 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18367 this.fireEvent('monthchange', this, date);
18371 if(!this.internalRender){
18372 var main = this.el.dom.firstChild;
18373 var w = main.offsetWidth;
18374 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18375 Roo.fly(main).setWidth(w);
18376 this.internalRender = true;
18377 // opera does not respect the auto grow header center column
18378 // then, after it gets a width opera refuses to recalculate
18379 // without a second pass
18380 if(Roo.isOpera && !this.secondPass){
18381 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18382 this.secondPass = true;
18383 this.update.defer(10, this, [date]);
18390 findCell : function(dt) {
18391 dt = dt.clearTime().getTime();
18393 this.cells.each(function(c){
18394 //Roo.log("check " +c.dateValue + '?=' + dt);
18395 if(c.dateValue == dt){
18405 findCells : function(ev) {
18406 var s = ev.start.clone().clearTime().getTime();
18408 var e= ev.end.clone().clearTime().getTime();
18411 this.cells.each(function(c){
18412 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18414 if(c.dateValue > e){
18417 if(c.dateValue < s){
18426 // findBestRow: function(cells)
18430 // for (var i =0 ; i < cells.length;i++) {
18431 // ret = Math.max(cells[i].rows || 0,ret);
18438 addItem : function(ev)
18440 // look for vertical location slot in
18441 var cells = this.findCells(ev);
18443 // ev.row = this.findBestRow(cells);
18445 // work out the location.
18449 for(var i =0; i < cells.length; i++) {
18451 cells[i].row = cells[0].row;
18454 cells[i].row = cells[i].row + 1;
18464 if (crow.start.getY() == cells[i].getY()) {
18466 crow.end = cells[i];
18483 cells[0].events.push(ev);
18485 this.calevents.push(ev);
18488 clearEvents: function() {
18490 if(!this.calevents){
18494 Roo.each(this.cells.elements, function(c){
18500 Roo.each(this.calevents, function(e) {
18501 Roo.each(e.els, function(el) {
18502 el.un('mouseenter' ,this.onEventEnter, this);
18503 el.un('mouseleave' ,this.onEventLeave, this);
18508 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18514 renderEvents: function()
18518 this.cells.each(function(c) {
18527 if(c.row != c.events.length){
18528 r = 4 - (4 - (c.row - c.events.length));
18531 c.events = ev.slice(0, r);
18532 c.more = ev.slice(r);
18534 if(c.more.length && c.more.length == 1){
18535 c.events.push(c.more.pop());
18538 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18542 this.cells.each(function(c) {
18544 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18547 for (var e = 0; e < c.events.length; e++){
18548 var ev = c.events[e];
18549 var rows = ev.rows;
18551 for(var i = 0; i < rows.length; i++) {
18553 // how many rows should it span..
18556 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18557 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18559 unselectable : "on",
18562 cls: 'fc-event-inner',
18566 // cls: 'fc-event-time',
18567 // html : cells.length > 1 ? '' : ev.time
18571 cls: 'fc-event-title',
18572 html : String.format('{0}', ev.title)
18579 cls: 'ui-resizable-handle ui-resizable-e',
18580 html : '  '
18587 cfg.cls += ' fc-event-start';
18589 if ((i+1) == rows.length) {
18590 cfg.cls += ' fc-event-end';
18593 var ctr = _this.el.select('.fc-event-container',true).first();
18594 var cg = ctr.createChild(cfg);
18596 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18597 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18599 var r = (c.more.length) ? 1 : 0;
18600 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
18601 cg.setWidth(ebox.right - sbox.x -2);
18603 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18604 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18605 cg.on('click', _this.onEventClick, _this, ev);
18616 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18617 style : 'position: absolute',
18618 unselectable : "on",
18621 cls: 'fc-event-inner',
18625 cls: 'fc-event-title',
18633 cls: 'ui-resizable-handle ui-resizable-e',
18634 html : '  '
18640 var ctr = _this.el.select('.fc-event-container',true).first();
18641 var cg = ctr.createChild(cfg);
18643 var sbox = c.select('.fc-day-content',true).first().getBox();
18644 var ebox = c.select('.fc-day-content',true).first().getBox();
18646 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
18647 cg.setWidth(ebox.right - sbox.x -2);
18649 cg.on('click', _this.onMoreEventClick, _this, c.more);
18659 onEventEnter: function (e, el,event,d) {
18660 this.fireEvent('evententer', this, el, event);
18663 onEventLeave: function (e, el,event,d) {
18664 this.fireEvent('eventleave', this, el, event);
18667 onEventClick: function (e, el,event,d) {
18668 this.fireEvent('eventclick', this, el, event);
18671 onMonthChange: function () {
18675 onMoreEventClick: function(e, el, more)
18679 this.calpopover.placement = 'right';
18680 this.calpopover.setTitle('More');
18682 this.calpopover.setContent('');
18684 var ctr = this.calpopover.el.select('.popover-content', true).first();
18686 Roo.each(more, function(m){
18688 cls : 'fc-event-hori fc-event-draggable',
18691 var cg = ctr.createChild(cfg);
18693 cg.on('click', _this.onEventClick, _this, m);
18696 this.calpopover.show(el);
18701 onLoad: function ()
18703 this.calevents = [];
18706 if(this.store.getCount() > 0){
18707 this.store.data.each(function(d){
18710 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18711 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18712 time : d.data.start_time,
18713 title : d.data.title,
18714 description : d.data.description,
18715 venue : d.data.venue
18720 this.renderEvents();
18722 if(this.calevents.length && this.loadMask){
18723 this.maskEl.hide();
18727 onBeforeLoad: function()
18729 this.clearEvents();
18731 this.maskEl.show();
18745 * @class Roo.bootstrap.Popover
18746 * @extends Roo.bootstrap.Component
18747 * Bootstrap Popover class
18748 * @cfg {String} html contents of the popover (or false to use children..)
18749 * @cfg {String} title of popover (or false to hide)
18750 * @cfg {String} placement how it is placed
18751 * @cfg {String} trigger click || hover (or false to trigger manually)
18752 * @cfg {String} over what (parent or false to trigger manually.)
18753 * @cfg {Number} delay - delay before showing
18756 * Create a new Popover
18757 * @param {Object} config The config object
18760 Roo.bootstrap.Popover = function(config){
18761 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18767 * After the popover show
18769 * @param {Roo.bootstrap.Popover} this
18774 * After the popover hide
18776 * @param {Roo.bootstrap.Popover} this
18782 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18784 title: 'Fill in a title',
18787 placement : 'right',
18788 trigger : 'hover', // hover
18794 can_build_overlaid : false,
18796 getChildContainer : function()
18798 return this.el.select('.popover-content',true).first();
18801 getAutoCreate : function(){
18804 cls : 'popover roo-dynamic',
18805 style: 'display:block',
18811 cls : 'popover-inner',
18815 cls: 'popover-title popover-header',
18819 cls : 'popover-content popover-body',
18830 setTitle: function(str)
18833 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18835 setContent: function(str)
18838 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18840 // as it get's added to the bottom of the page.
18841 onRender : function(ct, position)
18843 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18845 var cfg = Roo.apply({}, this.getAutoCreate());
18849 cfg.cls += ' ' + this.cls;
18852 cfg.style = this.style;
18854 //Roo.log("adding to ");
18855 this.el = Roo.get(document.body).createChild(cfg, position);
18856 // Roo.log(this.el);
18861 initEvents : function()
18863 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18864 this.el.enableDisplayMode('block');
18866 if (this.over === false) {
18869 if (this.triggers === false) {
18872 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18873 var triggers = this.trigger ? this.trigger.split(' ') : [];
18874 Roo.each(triggers, function(trigger) {
18876 if (trigger == 'click') {
18877 on_el.on('click', this.toggle, this);
18878 } else if (trigger != 'manual') {
18879 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18880 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18882 on_el.on(eventIn ,this.enter, this);
18883 on_el.on(eventOut, this.leave, this);
18894 toggle : function () {
18895 this.hoverState == 'in' ? this.leave() : this.enter();
18898 enter : function () {
18900 clearTimeout(this.timeout);
18902 this.hoverState = 'in';
18904 if (!this.delay || !this.delay.show) {
18909 this.timeout = setTimeout(function () {
18910 if (_t.hoverState == 'in') {
18913 }, this.delay.show)
18916 leave : function() {
18917 clearTimeout(this.timeout);
18919 this.hoverState = 'out';
18921 if (!this.delay || !this.delay.hide) {
18926 this.timeout = setTimeout(function () {
18927 if (_t.hoverState == 'out') {
18930 }, this.delay.hide)
18933 show : function (on_el)
18936 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18940 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18941 if (this.html !== false) {
18942 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18944 this.el.removeClass([
18945 'fade','top','bottom', 'left', 'right','in',
18946 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18948 if (!this.title.length) {
18949 this.el.select('.popover-title',true).hide();
18952 var placement = typeof this.placement == 'function' ?
18953 this.placement.call(this, this.el, on_el) :
18956 var autoToken = /\s?auto?\s?/i;
18957 var autoPlace = autoToken.test(placement);
18959 placement = placement.replace(autoToken, '') || 'top';
18963 //this.el.setXY([0,0]);
18965 this.el.dom.style.display='block';
18966 this.el.addClass(placement);
18968 //this.el.appendTo(on_el);
18970 var p = this.getPosition();
18971 var box = this.el.getBox();
18976 var align = Roo.bootstrap.Popover.alignment[placement];
18979 this.el.alignTo(on_el, align[0],align[1]);
18980 //var arrow = this.el.select('.arrow',true).first();
18981 //arrow.set(align[2],
18983 this.el.addClass('in');
18986 if (this.el.hasClass('fade')) {
18990 this.hoverState = 'in';
18992 this.fireEvent('show', this);
18997 this.el.setXY([0,0]);
18998 this.el.removeClass('in');
19000 this.hoverState = null;
19002 this.fireEvent('hide', this);
19007 Roo.bootstrap.Popover.alignment = {
19008 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19009 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19010 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19011 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19022 * @class Roo.bootstrap.Progress
19023 * @extends Roo.bootstrap.Component
19024 * Bootstrap Progress class
19025 * @cfg {Boolean} striped striped of the progress bar
19026 * @cfg {Boolean} active animated of the progress bar
19030 * Create a new Progress
19031 * @param {Object} config The config object
19034 Roo.bootstrap.Progress = function(config){
19035 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19038 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19043 getAutoCreate : function(){
19051 cfg.cls += ' progress-striped';
19055 cfg.cls += ' active';
19074 * @class Roo.bootstrap.ProgressBar
19075 * @extends Roo.bootstrap.Component
19076 * Bootstrap ProgressBar class
19077 * @cfg {Number} aria_valuenow aria-value now
19078 * @cfg {Number} aria_valuemin aria-value min
19079 * @cfg {Number} aria_valuemax aria-value max
19080 * @cfg {String} label label for the progress bar
19081 * @cfg {String} panel (success | info | warning | danger )
19082 * @cfg {String} role role of the progress bar
19083 * @cfg {String} sr_only text
19087 * Create a new ProgressBar
19088 * @param {Object} config The config object
19091 Roo.bootstrap.ProgressBar = function(config){
19092 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19095 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19099 aria_valuemax : 100,
19105 getAutoCreate : function()
19110 cls: 'progress-bar',
19111 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19123 cfg.role = this.role;
19126 if(this.aria_valuenow){
19127 cfg['aria-valuenow'] = this.aria_valuenow;
19130 if(this.aria_valuemin){
19131 cfg['aria-valuemin'] = this.aria_valuemin;
19134 if(this.aria_valuemax){
19135 cfg['aria-valuemax'] = this.aria_valuemax;
19138 if(this.label && !this.sr_only){
19139 cfg.html = this.label;
19143 cfg.cls += ' progress-bar-' + this.panel;
19149 update : function(aria_valuenow)
19151 this.aria_valuenow = aria_valuenow;
19153 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19168 * @class Roo.bootstrap.TabGroup
19169 * @extends Roo.bootstrap.Column
19170 * Bootstrap Column class
19171 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19172 * @cfg {Boolean} carousel true to make the group behave like a carousel
19173 * @cfg {Boolean} bullets show bullets for the panels
19174 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19175 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19176 * @cfg {Boolean} showarrow (true|false) show arrow default true
19179 * Create a new TabGroup
19180 * @param {Object} config The config object
19183 Roo.bootstrap.TabGroup = function(config){
19184 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19186 this.navId = Roo.id();
19189 Roo.bootstrap.TabGroup.register(this);
19193 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19196 transition : false,
19201 slideOnTouch : false,
19204 getAutoCreate : function()
19206 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19208 cfg.cls += ' tab-content';
19210 if (this.carousel) {
19211 cfg.cls += ' carousel slide';
19214 cls : 'carousel-inner',
19218 if(this.bullets && !Roo.isTouch){
19221 cls : 'carousel-bullets',
19225 if(this.bullets_cls){
19226 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19233 cfg.cn[0].cn.push(bullets);
19236 if(this.showarrow){
19237 cfg.cn[0].cn.push({
19239 class : 'carousel-arrow',
19243 class : 'carousel-prev',
19247 class : 'fa fa-chevron-left'
19253 class : 'carousel-next',
19257 class : 'fa fa-chevron-right'
19270 initEvents: function()
19272 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19273 // this.el.on("touchstart", this.onTouchStart, this);
19276 if(this.autoslide){
19279 this.slideFn = window.setInterval(function() {
19280 _this.showPanelNext();
19284 if(this.showarrow){
19285 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19286 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19292 // onTouchStart : function(e, el, o)
19294 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19298 // this.showPanelNext();
19302 getChildContainer : function()
19304 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19308 * register a Navigation item
19309 * @param {Roo.bootstrap.NavItem} the navitem to add
19311 register : function(item)
19313 this.tabs.push( item);
19314 item.navId = this.navId; // not really needed..
19319 getActivePanel : function()
19322 Roo.each(this.tabs, function(t) {
19332 getPanelByName : function(n)
19335 Roo.each(this.tabs, function(t) {
19336 if (t.tabId == n) {
19344 indexOfPanel : function(p)
19347 Roo.each(this.tabs, function(t,i) {
19348 if (t.tabId == p.tabId) {
19357 * show a specific panel
19358 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19359 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19361 showPanel : function (pan)
19363 if(this.transition || typeof(pan) == 'undefined'){
19364 Roo.log("waiting for the transitionend");
19368 if (typeof(pan) == 'number') {
19369 pan = this.tabs[pan];
19372 if (typeof(pan) == 'string') {
19373 pan = this.getPanelByName(pan);
19376 var cur = this.getActivePanel();
19379 Roo.log('pan or acitve pan is undefined');
19383 if (pan.tabId == this.getActivePanel().tabId) {
19387 if (false === cur.fireEvent('beforedeactivate')) {
19391 if(this.bullets > 0 && !Roo.isTouch){
19392 this.setActiveBullet(this.indexOfPanel(pan));
19395 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19397 //class="carousel-item carousel-item-next carousel-item-left"
19399 this.transition = true;
19400 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
19401 var lr = dir == 'next' ? 'left' : 'right';
19402 pan.el.addClass(dir); // or prev
19403 pan.el.addClass('carousel-item-' + dir); // or prev
19404 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19405 cur.el.addClass(lr); // or right
19406 pan.el.addClass(lr);
19407 cur.el.addClass('carousel-item-' +lr); // or right
19408 pan.el.addClass('carousel-item-' +lr);
19412 cur.el.on('transitionend', function() {
19413 Roo.log("trans end?");
19415 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19416 pan.setActive(true);
19418 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19419 cur.setActive(false);
19421 _this.transition = false;
19423 }, this, { single: true } );
19428 cur.setActive(false);
19429 pan.setActive(true);
19434 showPanelNext : function()
19436 var i = this.indexOfPanel(this.getActivePanel());
19438 if (i >= this.tabs.length - 1 && !this.autoslide) {
19442 if (i >= this.tabs.length - 1 && this.autoslide) {
19446 this.showPanel(this.tabs[i+1]);
19449 showPanelPrev : function()
19451 var i = this.indexOfPanel(this.getActivePanel());
19453 if (i < 1 && !this.autoslide) {
19457 if (i < 1 && this.autoslide) {
19458 i = this.tabs.length;
19461 this.showPanel(this.tabs[i-1]);
19465 addBullet: function()
19467 if(!this.bullets || Roo.isTouch){
19470 var ctr = this.el.select('.carousel-bullets',true).first();
19471 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19472 var bullet = ctr.createChild({
19473 cls : 'bullet bullet-' + i
19474 },ctr.dom.lastChild);
19479 bullet.on('click', (function(e, el, o, ii, t){
19481 e.preventDefault();
19483 this.showPanel(ii);
19485 if(this.autoslide && this.slideFn){
19486 clearInterval(this.slideFn);
19487 this.slideFn = window.setInterval(function() {
19488 _this.showPanelNext();
19492 }).createDelegate(this, [i, bullet], true));
19497 setActiveBullet : function(i)
19503 Roo.each(this.el.select('.bullet', true).elements, function(el){
19504 el.removeClass('selected');
19507 var bullet = this.el.select('.bullet-' + i, true).first();
19513 bullet.addClass('selected');
19524 Roo.apply(Roo.bootstrap.TabGroup, {
19528 * register a Navigation Group
19529 * @param {Roo.bootstrap.NavGroup} the navgroup to add
19531 register : function(navgrp)
19533 this.groups[navgrp.navId] = navgrp;
19537 * fetch a Navigation Group based on the navigation ID
19538 * if one does not exist , it will get created.
19539 * @param {string} the navgroup to add
19540 * @returns {Roo.bootstrap.NavGroup} the navgroup
19542 get: function(navId) {
19543 if (typeof(this.groups[navId]) == 'undefined') {
19544 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19546 return this.groups[navId] ;
19561 * @class Roo.bootstrap.TabPanel
19562 * @extends Roo.bootstrap.Component
19563 * Bootstrap TabPanel class
19564 * @cfg {Boolean} active panel active
19565 * @cfg {String} html panel content
19566 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19567 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19568 * @cfg {String} href click to link..
19572 * Create a new TabPanel
19573 * @param {Object} config The config object
19576 Roo.bootstrap.TabPanel = function(config){
19577 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19581 * Fires when the active status changes
19582 * @param {Roo.bootstrap.TabPanel} this
19583 * @param {Boolean} state the new state
19588 * @event beforedeactivate
19589 * Fires before a tab is de-activated - can be used to do validation on a form.
19590 * @param {Roo.bootstrap.TabPanel} this
19591 * @return {Boolean} false if there is an error
19594 'beforedeactivate': true
19597 this.tabId = this.tabId || Roo.id();
19601 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
19609 getAutoCreate : function(){
19614 // item is needed for carousel - not sure if it has any effect otherwise
19615 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19616 html: this.html || ''
19620 cfg.cls += ' active';
19624 cfg.tabId = this.tabId;
19632 initEvents: function()
19634 var p = this.parent();
19636 this.navId = this.navId || p.navId;
19638 if (typeof(this.navId) != 'undefined') {
19639 // not really needed.. but just in case.. parent should be a NavGroup.
19640 var tg = Roo.bootstrap.TabGroup.get(this.navId);
19644 var i = tg.tabs.length - 1;
19646 if(this.active && tg.bullets > 0 && i < tg.bullets){
19647 tg.setActiveBullet(i);
19651 this.el.on('click', this.onClick, this);
19654 this.el.on("touchstart", this.onTouchStart, this);
19655 this.el.on("touchmove", this.onTouchMove, this);
19656 this.el.on("touchend", this.onTouchEnd, this);
19661 onRender : function(ct, position)
19663 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19666 setActive : function(state)
19668 Roo.log("panel - set active " + this.tabId + "=" + state);
19670 this.active = state;
19672 this.el.removeClass('active');
19674 } else if (!this.el.hasClass('active')) {
19675 this.el.addClass('active');
19678 this.fireEvent('changed', this, state);
19681 onClick : function(e)
19683 e.preventDefault();
19685 if(!this.href.length){
19689 window.location.href = this.href;
19698 onTouchStart : function(e)
19700 this.swiping = false;
19702 this.startX = e.browserEvent.touches[0].clientX;
19703 this.startY = e.browserEvent.touches[0].clientY;
19706 onTouchMove : function(e)
19708 this.swiping = true;
19710 this.endX = e.browserEvent.touches[0].clientX;
19711 this.endY = e.browserEvent.touches[0].clientY;
19714 onTouchEnd : function(e)
19721 var tabGroup = this.parent();
19723 if(this.endX > this.startX){ // swiping right
19724 tabGroup.showPanelPrev();
19728 if(this.startX > this.endX){ // swiping left
19729 tabGroup.showPanelNext();
19748 * @class Roo.bootstrap.DateField
19749 * @extends Roo.bootstrap.Input
19750 * Bootstrap DateField class
19751 * @cfg {Number} weekStart default 0
19752 * @cfg {String} viewMode default empty, (months|years)
19753 * @cfg {String} minViewMode default empty, (months|years)
19754 * @cfg {Number} startDate default -Infinity
19755 * @cfg {Number} endDate default Infinity
19756 * @cfg {Boolean} todayHighlight default false
19757 * @cfg {Boolean} todayBtn default false
19758 * @cfg {Boolean} calendarWeeks default false
19759 * @cfg {Object} daysOfWeekDisabled default empty
19760 * @cfg {Boolean} singleMode default false (true | false)
19762 * @cfg {Boolean} keyboardNavigation default true
19763 * @cfg {String} language default en
19766 * Create a new DateField
19767 * @param {Object} config The config object
19770 Roo.bootstrap.DateField = function(config){
19771 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19775 * Fires when this field show.
19776 * @param {Roo.bootstrap.DateField} this
19777 * @param {Mixed} date The date value
19782 * Fires when this field hide.
19783 * @param {Roo.bootstrap.DateField} this
19784 * @param {Mixed} date The date value
19789 * Fires when select a date.
19790 * @param {Roo.bootstrap.DateField} this
19791 * @param {Mixed} date The date value
19795 * @event beforeselect
19796 * Fires when before select a date.
19797 * @param {Roo.bootstrap.DateField} this
19798 * @param {Mixed} date The date value
19800 beforeselect : true
19804 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19807 * @cfg {String} format
19808 * The default date format string which can be overriden for localization support. The format must be
19809 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19813 * @cfg {String} altFormats
19814 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19815 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19817 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19825 todayHighlight : false,
19831 keyboardNavigation: true,
19833 calendarWeeks: false,
19835 startDate: -Infinity,
19839 daysOfWeekDisabled: [],
19843 singleMode : false,
19845 UTCDate: function()
19847 return new Date(Date.UTC.apply(Date, arguments));
19850 UTCToday: function()
19852 var today = new Date();
19853 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19856 getDate: function() {
19857 var d = this.getUTCDate();
19858 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19861 getUTCDate: function() {
19865 setDate: function(d) {
19866 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19869 setUTCDate: function(d) {
19871 this.setValue(this.formatDate(this.date));
19874 onRender: function(ct, position)
19877 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19879 this.language = this.language || 'en';
19880 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19881 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19883 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19884 this.format = this.format || 'm/d/y';
19885 this.isInline = false;
19886 this.isInput = true;
19887 this.component = this.el.select('.add-on', true).first() || false;
19888 this.component = (this.component && this.component.length === 0) ? false : this.component;
19889 this.hasInput = this.component && this.inputEl().length;
19891 if (typeof(this.minViewMode === 'string')) {
19892 switch (this.minViewMode) {
19894 this.minViewMode = 1;
19897 this.minViewMode = 2;
19900 this.minViewMode = 0;
19905 if (typeof(this.viewMode === 'string')) {
19906 switch (this.viewMode) {
19919 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19921 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19923 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19925 this.picker().on('mousedown', this.onMousedown, this);
19926 this.picker().on('click', this.onClick, this);
19928 this.picker().addClass('datepicker-dropdown');
19930 this.startViewMode = this.viewMode;
19932 if(this.singleMode){
19933 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19934 v.setVisibilityMode(Roo.Element.DISPLAY);
19938 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19939 v.setStyle('width', '189px');
19943 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19944 if(!this.calendarWeeks){
19949 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19950 v.attr('colspan', function(i, val){
19951 return parseInt(val) + 1;
19956 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19958 this.setStartDate(this.startDate);
19959 this.setEndDate(this.endDate);
19961 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19968 if(this.isInline) {
19973 picker : function()
19975 return this.pickerEl;
19976 // return this.el.select('.datepicker', true).first();
19979 fillDow: function()
19981 var dowCnt = this.weekStart;
19990 if(this.calendarWeeks){
19998 while (dowCnt < this.weekStart + 7) {
20002 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20006 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20009 fillMonths: function()
20012 var months = this.picker().select('>.datepicker-months td', true).first();
20014 months.dom.innerHTML = '';
20020 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20023 months.createChild(month);
20030 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;
20032 if (this.date < this.startDate) {
20033 this.viewDate = new Date(this.startDate);
20034 } else if (this.date > this.endDate) {
20035 this.viewDate = new Date(this.endDate);
20037 this.viewDate = new Date(this.date);
20045 var d = new Date(this.viewDate),
20046 year = d.getUTCFullYear(),
20047 month = d.getUTCMonth(),
20048 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20049 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20050 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20051 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20052 currentDate = this.date && this.date.valueOf(),
20053 today = this.UTCToday();
20055 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20057 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20059 // this.picker.select('>tfoot th.today').
20060 // .text(dates[this.language].today)
20061 // .toggle(this.todayBtn !== false);
20063 this.updateNavArrows();
20066 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20068 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20070 prevMonth.setUTCDate(day);
20072 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20074 var nextMonth = new Date(prevMonth);
20076 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20078 nextMonth = nextMonth.valueOf();
20080 var fillMonths = false;
20082 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20084 while(prevMonth.valueOf() <= nextMonth) {
20087 if (prevMonth.getUTCDay() === this.weekStart) {
20089 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20097 if(this.calendarWeeks){
20098 // ISO 8601: First week contains first thursday.
20099 // ISO also states week starts on Monday, but we can be more abstract here.
20101 // Start of current week: based on weekstart/current date
20102 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20103 // Thursday of this week
20104 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20105 // First Thursday of year, year from thursday
20106 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20107 // Calendar week: ms between thursdays, div ms per day, div 7 days
20108 calWeek = (th - yth) / 864e5 / 7 + 1;
20110 fillMonths.cn.push({
20118 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20120 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20123 if (this.todayHighlight &&
20124 prevMonth.getUTCFullYear() == today.getFullYear() &&
20125 prevMonth.getUTCMonth() == today.getMonth() &&
20126 prevMonth.getUTCDate() == today.getDate()) {
20127 clsName += ' today';
20130 if (currentDate && prevMonth.valueOf() === currentDate) {
20131 clsName += ' active';
20134 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20135 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20136 clsName += ' disabled';
20139 fillMonths.cn.push({
20141 cls: 'day ' + clsName,
20142 html: prevMonth.getDate()
20145 prevMonth.setDate(prevMonth.getDate()+1);
20148 var currentYear = this.date && this.date.getUTCFullYear();
20149 var currentMonth = this.date && this.date.getUTCMonth();
20151 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20153 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20154 v.removeClass('active');
20156 if(currentYear === year && k === currentMonth){
20157 v.addClass('active');
20160 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20161 v.addClass('disabled');
20167 year = parseInt(year/10, 10) * 10;
20169 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20171 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20174 for (var i = -1; i < 11; i++) {
20175 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20177 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20185 showMode: function(dir)
20188 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20191 Roo.each(this.picker().select('>div',true).elements, function(v){
20192 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20195 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20200 if(this.isInline) {
20204 this.picker().removeClass(['bottom', 'top']);
20206 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20208 * place to the top of element!
20212 this.picker().addClass('top');
20213 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20218 this.picker().addClass('bottom');
20220 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20223 parseDate : function(value)
20225 if(!value || value instanceof Date){
20228 var v = Date.parseDate(value, this.format);
20229 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20230 v = Date.parseDate(value, 'Y-m-d');
20232 if(!v && this.altFormats){
20233 if(!this.altFormatsArray){
20234 this.altFormatsArray = this.altFormats.split("|");
20236 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20237 v = Date.parseDate(value, this.altFormatsArray[i]);
20243 formatDate : function(date, fmt)
20245 return (!date || !(date instanceof Date)) ?
20246 date : date.dateFormat(fmt || this.format);
20249 onFocus : function()
20251 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20255 onBlur : function()
20257 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20259 var d = this.inputEl().getValue();
20266 showPopup : function()
20268 this.picker().show();
20272 this.fireEvent('showpopup', this, this.date);
20275 hidePopup : function()
20277 if(this.isInline) {
20280 this.picker().hide();
20281 this.viewMode = this.startViewMode;
20284 this.fireEvent('hidepopup', this, this.date);
20288 onMousedown: function(e)
20290 e.stopPropagation();
20291 e.preventDefault();
20296 Roo.bootstrap.DateField.superclass.keyup.call(this);
20300 setValue: function(v)
20302 if(this.fireEvent('beforeselect', this, v) !== false){
20303 var d = new Date(this.parseDate(v) ).clearTime();
20305 if(isNaN(d.getTime())){
20306 this.date = this.viewDate = '';
20307 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20311 v = this.formatDate(d);
20313 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20315 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20319 this.fireEvent('select', this, this.date);
20323 getValue: function()
20325 return this.formatDate(this.date);
20328 fireKey: function(e)
20330 if (!this.picker().isVisible()){
20331 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20337 var dateChanged = false,
20339 newDate, newViewDate;
20344 e.preventDefault();
20348 if (!this.keyboardNavigation) {
20351 dir = e.keyCode == 37 ? -1 : 1;
20354 newDate = this.moveYear(this.date, dir);
20355 newViewDate = this.moveYear(this.viewDate, dir);
20356 } else if (e.shiftKey){
20357 newDate = this.moveMonth(this.date, dir);
20358 newViewDate = this.moveMonth(this.viewDate, dir);
20360 newDate = new Date(this.date);
20361 newDate.setUTCDate(this.date.getUTCDate() + dir);
20362 newViewDate = new Date(this.viewDate);
20363 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20365 if (this.dateWithinRange(newDate)){
20366 this.date = newDate;
20367 this.viewDate = newViewDate;
20368 this.setValue(this.formatDate(this.date));
20370 e.preventDefault();
20371 dateChanged = true;
20376 if (!this.keyboardNavigation) {
20379 dir = e.keyCode == 38 ? -1 : 1;
20381 newDate = this.moveYear(this.date, dir);
20382 newViewDate = this.moveYear(this.viewDate, dir);
20383 } else if (e.shiftKey){
20384 newDate = this.moveMonth(this.date, dir);
20385 newViewDate = this.moveMonth(this.viewDate, dir);
20387 newDate = new Date(this.date);
20388 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20389 newViewDate = new Date(this.viewDate);
20390 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20392 if (this.dateWithinRange(newDate)){
20393 this.date = newDate;
20394 this.viewDate = newViewDate;
20395 this.setValue(this.formatDate(this.date));
20397 e.preventDefault();
20398 dateChanged = true;
20402 this.setValue(this.formatDate(this.date));
20404 e.preventDefault();
20407 this.setValue(this.formatDate(this.date));
20421 onClick: function(e)
20423 e.stopPropagation();
20424 e.preventDefault();
20426 var target = e.getTarget();
20428 if(target.nodeName.toLowerCase() === 'i'){
20429 target = Roo.get(target).dom.parentNode;
20432 var nodeName = target.nodeName;
20433 var className = target.className;
20434 var html = target.innerHTML;
20435 //Roo.log(nodeName);
20437 switch(nodeName.toLowerCase()) {
20439 switch(className) {
20445 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20446 switch(this.viewMode){
20448 this.viewDate = this.moveMonth(this.viewDate, dir);
20452 this.viewDate = this.moveYear(this.viewDate, dir);
20458 var date = new Date();
20459 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20461 this.setValue(this.formatDate(this.date));
20468 if (className.indexOf('disabled') < 0) {
20469 this.viewDate.setUTCDate(1);
20470 if (className.indexOf('month') > -1) {
20471 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20473 var year = parseInt(html, 10) || 0;
20474 this.viewDate.setUTCFullYear(year);
20478 if(this.singleMode){
20479 this.setValue(this.formatDate(this.viewDate));
20490 //Roo.log(className);
20491 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20492 var day = parseInt(html, 10) || 1;
20493 var year = this.viewDate.getUTCFullYear(),
20494 month = this.viewDate.getUTCMonth();
20496 if (className.indexOf('old') > -1) {
20503 } else if (className.indexOf('new') > -1) {
20511 //Roo.log([year,month,day]);
20512 this.date = this.UTCDate(year, month, day,0,0,0,0);
20513 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20515 //Roo.log(this.formatDate(this.date));
20516 this.setValue(this.formatDate(this.date));
20523 setStartDate: function(startDate)
20525 this.startDate = startDate || -Infinity;
20526 if (this.startDate !== -Infinity) {
20527 this.startDate = this.parseDate(this.startDate);
20530 this.updateNavArrows();
20533 setEndDate: function(endDate)
20535 this.endDate = endDate || Infinity;
20536 if (this.endDate !== Infinity) {
20537 this.endDate = this.parseDate(this.endDate);
20540 this.updateNavArrows();
20543 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20545 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20546 if (typeof(this.daysOfWeekDisabled) !== 'object') {
20547 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20549 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20550 return parseInt(d, 10);
20553 this.updateNavArrows();
20556 updateNavArrows: function()
20558 if(this.singleMode){
20562 var d = new Date(this.viewDate),
20563 year = d.getUTCFullYear(),
20564 month = d.getUTCMonth();
20566 Roo.each(this.picker().select('.prev', true).elements, function(v){
20568 switch (this.viewMode) {
20571 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20577 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20584 Roo.each(this.picker().select('.next', true).elements, function(v){
20586 switch (this.viewMode) {
20589 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20595 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20603 moveMonth: function(date, dir)
20608 var new_date = new Date(date.valueOf()),
20609 day = new_date.getUTCDate(),
20610 month = new_date.getUTCMonth(),
20611 mag = Math.abs(dir),
20613 dir = dir > 0 ? 1 : -1;
20616 // If going back one month, make sure month is not current month
20617 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20619 return new_date.getUTCMonth() == month;
20621 // If going forward one month, make sure month is as expected
20622 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20624 return new_date.getUTCMonth() != new_month;
20626 new_month = month + dir;
20627 new_date.setUTCMonth(new_month);
20628 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20629 if (new_month < 0 || new_month > 11) {
20630 new_month = (new_month + 12) % 12;
20633 // For magnitudes >1, move one month at a time...
20634 for (var i=0; i<mag; i++) {
20635 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20636 new_date = this.moveMonth(new_date, dir);
20638 // ...then reset the day, keeping it in the new month
20639 new_month = new_date.getUTCMonth();
20640 new_date.setUTCDate(day);
20642 return new_month != new_date.getUTCMonth();
20645 // Common date-resetting loop -- if date is beyond end of month, make it
20648 new_date.setUTCDate(--day);
20649 new_date.setUTCMonth(new_month);
20654 moveYear: function(date, dir)
20656 return this.moveMonth(date, dir*12);
20659 dateWithinRange: function(date)
20661 return date >= this.startDate && date <= this.endDate;
20667 this.picker().remove();
20670 validateValue : function(value)
20672 if(this.getVisibilityEl().hasClass('hidden')){
20676 if(value.length < 1) {
20677 if(this.allowBlank){
20683 if(value.length < this.minLength){
20686 if(value.length > this.maxLength){
20690 var vt = Roo.form.VTypes;
20691 if(!vt[this.vtype](value, this)){
20695 if(typeof this.validator == "function"){
20696 var msg = this.validator(value);
20702 if(this.regex && !this.regex.test(value)){
20706 if(typeof(this.parseDate(value)) == 'undefined'){
20710 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20714 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20724 this.date = this.viewDate = '';
20726 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20731 Roo.apply(Roo.bootstrap.DateField, {
20742 html: '<i class="fa fa-arrow-left"/>'
20752 html: '<i class="fa fa-arrow-right"/>'
20794 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20795 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20796 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20797 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20798 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20811 navFnc: 'FullYear',
20816 navFnc: 'FullYear',
20821 Roo.apply(Roo.bootstrap.DateField, {
20825 cls: 'datepicker dropdown-menu roo-dynamic',
20829 cls: 'datepicker-days',
20833 cls: 'table-condensed',
20835 Roo.bootstrap.DateField.head,
20839 Roo.bootstrap.DateField.footer
20846 cls: 'datepicker-months',
20850 cls: 'table-condensed',
20852 Roo.bootstrap.DateField.head,
20853 Roo.bootstrap.DateField.content,
20854 Roo.bootstrap.DateField.footer
20861 cls: 'datepicker-years',
20865 cls: 'table-condensed',
20867 Roo.bootstrap.DateField.head,
20868 Roo.bootstrap.DateField.content,
20869 Roo.bootstrap.DateField.footer
20888 * @class Roo.bootstrap.TimeField
20889 * @extends Roo.bootstrap.Input
20890 * Bootstrap DateField class
20894 * Create a new TimeField
20895 * @param {Object} config The config object
20898 Roo.bootstrap.TimeField = function(config){
20899 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20903 * Fires when this field show.
20904 * @param {Roo.bootstrap.DateField} thisthis
20905 * @param {Mixed} date The date value
20910 * Fires when this field hide.
20911 * @param {Roo.bootstrap.DateField} this
20912 * @param {Mixed} date The date value
20917 * Fires when select a date.
20918 * @param {Roo.bootstrap.DateField} this
20919 * @param {Mixed} date The date value
20925 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20928 * @cfg {String} format
20929 * The default time format string which can be overriden for localization support. The format must be
20930 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20934 onRender: function(ct, position)
20937 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20939 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20941 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20943 this.pop = this.picker().select('>.datepicker-time',true).first();
20944 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20946 this.picker().on('mousedown', this.onMousedown, this);
20947 this.picker().on('click', this.onClick, this);
20949 this.picker().addClass('datepicker-dropdown');
20954 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20955 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20956 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20957 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20958 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20959 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20963 fireKey: function(e){
20964 if (!this.picker().isVisible()){
20965 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20971 e.preventDefault();
20979 this.onTogglePeriod();
20982 this.onIncrementMinutes();
20985 this.onDecrementMinutes();
20994 onClick: function(e) {
20995 e.stopPropagation();
20996 e.preventDefault();
20999 picker : function()
21001 return this.el.select('.datepicker', true).first();
21004 fillTime: function()
21006 var time = this.pop.select('tbody', true).first();
21008 time.dom.innerHTML = '';
21023 cls: 'hours-up glyphicon glyphicon-chevron-up'
21043 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21064 cls: 'timepicker-hour',
21079 cls: 'timepicker-minute',
21094 cls: 'btn btn-primary period',
21116 cls: 'hours-down glyphicon glyphicon-chevron-down'
21136 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21154 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21161 var hours = this.time.getHours();
21162 var minutes = this.time.getMinutes();
21175 hours = hours - 12;
21179 hours = '0' + hours;
21183 minutes = '0' + minutes;
21186 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21187 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21188 this.pop.select('button', true).first().dom.innerHTML = period;
21194 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21196 var cls = ['bottom'];
21198 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21205 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21210 this.picker().addClass(cls.join('-'));
21214 Roo.each(cls, function(c){
21216 _this.picker().setTop(_this.inputEl().getHeight());
21220 _this.picker().setTop(0 - _this.picker().getHeight());
21225 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21229 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21236 onFocus : function()
21238 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21242 onBlur : function()
21244 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21250 this.picker().show();
21255 this.fireEvent('show', this, this.date);
21260 this.picker().hide();
21263 this.fireEvent('hide', this, this.date);
21266 setTime : function()
21269 this.setValue(this.time.format(this.format));
21271 this.fireEvent('select', this, this.date);
21276 onMousedown: function(e){
21277 e.stopPropagation();
21278 e.preventDefault();
21281 onIncrementHours: function()
21283 Roo.log('onIncrementHours');
21284 this.time = this.time.add(Date.HOUR, 1);
21289 onDecrementHours: function()
21291 Roo.log('onDecrementHours');
21292 this.time = this.time.add(Date.HOUR, -1);
21296 onIncrementMinutes: function()
21298 Roo.log('onIncrementMinutes');
21299 this.time = this.time.add(Date.MINUTE, 1);
21303 onDecrementMinutes: function()
21305 Roo.log('onDecrementMinutes');
21306 this.time = this.time.add(Date.MINUTE, -1);
21310 onTogglePeriod: function()
21312 Roo.log('onTogglePeriod');
21313 this.time = this.time.add(Date.HOUR, 12);
21320 Roo.apply(Roo.bootstrap.TimeField, {
21350 cls: 'btn btn-info ok',
21362 Roo.apply(Roo.bootstrap.TimeField, {
21366 cls: 'datepicker dropdown-menu',
21370 cls: 'datepicker-time',
21374 cls: 'table-condensed',
21376 Roo.bootstrap.TimeField.content,
21377 Roo.bootstrap.TimeField.footer
21396 * @class Roo.bootstrap.MonthField
21397 * @extends Roo.bootstrap.Input
21398 * Bootstrap MonthField class
21400 * @cfg {String} language default en
21403 * Create a new MonthField
21404 * @param {Object} config The config object
21407 Roo.bootstrap.MonthField = function(config){
21408 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21413 * Fires when this field show.
21414 * @param {Roo.bootstrap.MonthField} this
21415 * @param {Mixed} date The date value
21420 * Fires when this field hide.
21421 * @param {Roo.bootstrap.MonthField} this
21422 * @param {Mixed} date The date value
21427 * Fires when select a date.
21428 * @param {Roo.bootstrap.MonthField} this
21429 * @param {String} oldvalue The old value
21430 * @param {String} newvalue The new value
21436 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
21438 onRender: function(ct, position)
21441 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21443 this.language = this.language || 'en';
21444 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21445 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21447 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21448 this.isInline = false;
21449 this.isInput = true;
21450 this.component = this.el.select('.add-on', true).first() || false;
21451 this.component = (this.component && this.component.length === 0) ? false : this.component;
21452 this.hasInput = this.component && this.inputEL().length;
21454 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21456 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21458 this.picker().on('mousedown', this.onMousedown, this);
21459 this.picker().on('click', this.onClick, this);
21461 this.picker().addClass('datepicker-dropdown');
21463 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21464 v.setStyle('width', '189px');
21471 if(this.isInline) {
21477 setValue: function(v, suppressEvent)
21479 var o = this.getValue();
21481 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21485 if(suppressEvent !== true){
21486 this.fireEvent('select', this, o, v);
21491 getValue: function()
21496 onClick: function(e)
21498 e.stopPropagation();
21499 e.preventDefault();
21501 var target = e.getTarget();
21503 if(target.nodeName.toLowerCase() === 'i'){
21504 target = Roo.get(target).dom.parentNode;
21507 var nodeName = target.nodeName;
21508 var className = target.className;
21509 var html = target.innerHTML;
21511 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21515 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21517 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21523 picker : function()
21525 return this.pickerEl;
21528 fillMonths: function()
21531 var months = this.picker().select('>.datepicker-months td', true).first();
21533 months.dom.innerHTML = '';
21539 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21542 months.createChild(month);
21551 if(typeof(this.vIndex) == 'undefined' && this.value.length){
21552 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21555 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21556 e.removeClass('active');
21558 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21559 e.addClass('active');
21566 if(this.isInline) {
21570 this.picker().removeClass(['bottom', 'top']);
21572 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21574 * place to the top of element!
21578 this.picker().addClass('top');
21579 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21584 this.picker().addClass('bottom');
21586 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21589 onFocus : function()
21591 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21595 onBlur : function()
21597 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21599 var d = this.inputEl().getValue();
21608 this.picker().show();
21609 this.picker().select('>.datepicker-months', true).first().show();
21613 this.fireEvent('show', this, this.date);
21618 if(this.isInline) {
21621 this.picker().hide();
21622 this.fireEvent('hide', this, this.date);
21626 onMousedown: function(e)
21628 e.stopPropagation();
21629 e.preventDefault();
21634 Roo.bootstrap.MonthField.superclass.keyup.call(this);
21638 fireKey: function(e)
21640 if (!this.picker().isVisible()){
21641 if (e.keyCode == 27) {// allow escape to hide and re-show picker
21652 e.preventDefault();
21656 dir = e.keyCode == 37 ? -1 : 1;
21658 this.vIndex = this.vIndex + dir;
21660 if(this.vIndex < 0){
21664 if(this.vIndex > 11){
21668 if(isNaN(this.vIndex)){
21672 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21678 dir = e.keyCode == 38 ? -1 : 1;
21680 this.vIndex = this.vIndex + dir * 4;
21682 if(this.vIndex < 0){
21686 if(this.vIndex > 11){
21690 if(isNaN(this.vIndex)){
21694 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21699 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21700 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21704 e.preventDefault();
21707 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21708 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21724 this.picker().remove();
21729 Roo.apply(Roo.bootstrap.MonthField, {
21748 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21749 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21754 Roo.apply(Roo.bootstrap.MonthField, {
21758 cls: 'datepicker dropdown-menu roo-dynamic',
21762 cls: 'datepicker-months',
21766 cls: 'table-condensed',
21768 Roo.bootstrap.DateField.content
21788 * @class Roo.bootstrap.CheckBox
21789 * @extends Roo.bootstrap.Input
21790 * Bootstrap CheckBox class
21792 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21793 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21794 * @cfg {String} boxLabel The text that appears beside the checkbox
21795 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21796 * @cfg {Boolean} checked initnal the element
21797 * @cfg {Boolean} inline inline the element (default false)
21798 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21799 * @cfg {String} tooltip label tooltip
21802 * Create a new CheckBox
21803 * @param {Object} config The config object
21806 Roo.bootstrap.CheckBox = function(config){
21807 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21812 * Fires when the element is checked or unchecked.
21813 * @param {Roo.bootstrap.CheckBox} this This input
21814 * @param {Boolean} checked The new checked value
21819 * Fires when the element is click.
21820 * @param {Roo.bootstrap.CheckBox} this This input
21827 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21829 inputType: 'checkbox',
21838 // checkbox success does not make any sense really..
21843 getAutoCreate : function()
21845 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21851 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21854 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
21860 type : this.inputType,
21861 value : this.inputValue,
21862 cls : 'roo-' + this.inputType, //'form-box',
21863 placeholder : this.placeholder || ''
21867 if(this.inputType != 'radio'){
21871 cls : 'roo-hidden-value',
21872 value : this.checked ? this.inputValue : this.valueOff
21877 if (this.weight) { // Validity check?
21878 cfg.cls += " " + this.inputType + "-" + this.weight;
21881 if (this.disabled) {
21882 input.disabled=true;
21886 input.checked = this.checked;
21891 input.name = this.name;
21893 if(this.inputType != 'radio'){
21894 hidden.name = this.name;
21895 input.name = '_hidden_' + this.name;
21900 input.cls += ' input-' + this.size;
21905 ['xs','sm','md','lg'].map(function(size){
21906 if (settings[size]) {
21907 cfg.cls += ' col-' + size + '-' + settings[size];
21911 var inputblock = input;
21913 if (this.before || this.after) {
21916 cls : 'input-group',
21921 inputblock.cn.push({
21923 cls : 'input-group-addon',
21928 inputblock.cn.push(input);
21930 if(this.inputType != 'radio'){
21931 inputblock.cn.push(hidden);
21935 inputblock.cn.push({
21937 cls : 'input-group-addon',
21943 var boxLabelCfg = false;
21949 //'for': id, // box label is handled by onclick - so no for...
21951 html: this.boxLabel
21954 boxLabelCfg.tooltip = this.tooltip;
21960 if (align ==='left' && this.fieldLabel.length) {
21961 // Roo.log("left and has label");
21966 cls : 'control-label',
21967 html : this.fieldLabel
21978 cfg.cn[1].cn.push(boxLabelCfg);
21981 if(this.labelWidth > 12){
21982 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21985 if(this.labelWidth < 13 && this.labelmd == 0){
21986 this.labelmd = this.labelWidth;
21989 if(this.labellg > 0){
21990 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21991 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21994 if(this.labelmd > 0){
21995 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21996 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21999 if(this.labelsm > 0){
22000 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22001 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22004 if(this.labelxs > 0){
22005 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22006 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22009 } else if ( this.fieldLabel.length) {
22010 // Roo.log(" label");
22014 tag: this.boxLabel ? 'span' : 'label',
22016 cls: 'control-label box-input-label',
22017 //cls : 'input-group-addon',
22018 html : this.fieldLabel
22025 cfg.cn.push(boxLabelCfg);
22030 // Roo.log(" no label && no align");
22031 cfg.cn = [ inputblock ] ;
22033 cfg.cn.push(boxLabelCfg);
22041 if(this.inputType != 'radio'){
22042 cfg.cn.push(hidden);
22050 * return the real input element.
22052 inputEl: function ()
22054 return this.el.select('input.roo-' + this.inputType,true).first();
22056 hiddenEl: function ()
22058 return this.el.select('input.roo-hidden-value',true).first();
22061 labelEl: function()
22063 return this.el.select('label.control-label',true).first();
22065 /* depricated... */
22069 return this.labelEl();
22072 boxLabelEl: function()
22074 return this.el.select('label.box-label',true).first();
22077 initEvents : function()
22079 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22081 this.inputEl().on('click', this.onClick, this);
22083 if (this.boxLabel) {
22084 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22087 this.startValue = this.getValue();
22090 Roo.bootstrap.CheckBox.register(this);
22094 onClick : function(e)
22096 if(this.fireEvent('click', this, e) !== false){
22097 this.setChecked(!this.checked);
22102 setChecked : function(state,suppressEvent)
22104 this.startValue = this.getValue();
22106 if(this.inputType == 'radio'){
22108 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22109 e.dom.checked = false;
22112 this.inputEl().dom.checked = true;
22114 this.inputEl().dom.value = this.inputValue;
22116 if(suppressEvent !== true){
22117 this.fireEvent('check', this, true);
22125 this.checked = state;
22127 this.inputEl().dom.checked = state;
22130 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22132 if(suppressEvent !== true){
22133 this.fireEvent('check', this, state);
22139 getValue : function()
22141 if(this.inputType == 'radio'){
22142 return this.getGroupValue();
22145 return this.hiddenEl().dom.value;
22149 getGroupValue : function()
22151 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22155 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22158 setValue : function(v,suppressEvent)
22160 if(this.inputType == 'radio'){
22161 this.setGroupValue(v, suppressEvent);
22165 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22170 setGroupValue : function(v, suppressEvent)
22172 this.startValue = this.getValue();
22174 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22175 e.dom.checked = false;
22177 if(e.dom.value == v){
22178 e.dom.checked = true;
22182 if(suppressEvent !== true){
22183 this.fireEvent('check', this, true);
22191 validate : function()
22193 if(this.getVisibilityEl().hasClass('hidden')){
22199 (this.inputType == 'radio' && this.validateRadio()) ||
22200 (this.inputType == 'checkbox' && this.validateCheckbox())
22206 this.markInvalid();
22210 validateRadio : function()
22212 if(this.getVisibilityEl().hasClass('hidden')){
22216 if(this.allowBlank){
22222 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22223 if(!e.dom.checked){
22235 validateCheckbox : function()
22238 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22239 //return (this.getValue() == this.inputValue) ? true : false;
22242 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22250 for(var i in group){
22251 if(group[i].el.isVisible(true)){
22259 for(var i in group){
22264 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22271 * Mark this field as valid
22273 markValid : function()
22277 this.fireEvent('valid', this);
22279 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22282 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22289 if(this.inputType == 'radio'){
22290 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22291 var fg = e.findParent('.form-group', false, true);
22292 if (Roo.bootstrap.version == 3) {
22293 fg.removeClass([_this.invalidClass, _this.validClass]);
22294 fg.addClass(_this.validClass);
22296 fg.removeClass(['is-valid', 'is-invalid']);
22297 fg.addClass('is-valid');
22305 var fg = this.el.findParent('.form-group', false, true);
22306 if (Roo.bootstrap.version == 3) {
22307 fg.removeClass([this.invalidClass, this.validClass]);
22308 fg.addClass(this.validClass);
22310 fg.removeClass(['is-valid', 'is-invalid']);
22311 fg.addClass('is-valid');
22316 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22322 for(var i in group){
22323 var fg = group[i].el.findParent('.form-group', false, true);
22324 if (Roo.bootstrap.version == 3) {
22325 fg.removeClass([this.invalidClass, this.validClass]);
22326 fg.addClass(this.validClass);
22328 fg.removeClass(['is-valid', 'is-invalid']);
22329 fg.addClass('is-valid');
22335 * Mark this field as invalid
22336 * @param {String} msg The validation message
22338 markInvalid : function(msg)
22340 if(this.allowBlank){
22346 this.fireEvent('invalid', this, msg);
22348 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22351 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22355 label.markInvalid();
22358 if(this.inputType == 'radio'){
22360 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22361 var fg = e.findParent('.form-group', false, true);
22362 if (Roo.bootstrap.version == 3) {
22363 fg.removeClass([_this.invalidClass, _this.validClass]);
22364 fg.addClass(_this.invalidClass);
22366 fg.removeClass(['is-invalid', 'is-valid']);
22367 fg.addClass('is-invalid');
22375 var fg = this.el.findParent('.form-group', false, true);
22376 if (Roo.bootstrap.version == 3) {
22377 fg.removeClass([_this.invalidClass, _this.validClass]);
22378 fg.addClass(_this.invalidClass);
22380 fg.removeClass(['is-invalid', 'is-valid']);
22381 fg.addClass('is-invalid');
22386 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22392 for(var i in group){
22393 var fg = group[i].el.findParent('.form-group', false, true);
22394 if (Roo.bootstrap.version == 3) {
22395 fg.removeClass([_this.invalidClass, _this.validClass]);
22396 fg.addClass(_this.invalidClass);
22398 fg.removeClass(['is-invalid', 'is-valid']);
22399 fg.addClass('is-invalid');
22405 clearInvalid : function()
22407 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22409 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22411 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22413 if (label && label.iconEl) {
22414 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22415 label.iconEl.removeClass(['is-invalid', 'is-valid']);
22419 disable : function()
22421 if(this.inputType != 'radio'){
22422 Roo.bootstrap.CheckBox.superclass.disable.call(this);
22429 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22430 _this.getActionEl().addClass(this.disabledClass);
22431 e.dom.disabled = true;
22435 this.disabled = true;
22436 this.fireEvent("disable", this);
22440 enable : function()
22442 if(this.inputType != 'radio'){
22443 Roo.bootstrap.CheckBox.superclass.enable.call(this);
22450 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22451 _this.getActionEl().removeClass(this.disabledClass);
22452 e.dom.disabled = false;
22456 this.disabled = false;
22457 this.fireEvent("enable", this);
22461 setBoxLabel : function(v)
22466 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22472 Roo.apply(Roo.bootstrap.CheckBox, {
22477 * register a CheckBox Group
22478 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22480 register : function(checkbox)
22482 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22483 this.groups[checkbox.groupId] = {};
22486 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22490 this.groups[checkbox.groupId][checkbox.name] = checkbox;
22494 * fetch a CheckBox Group based on the group ID
22495 * @param {string} the group ID
22496 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22498 get: function(groupId) {
22499 if (typeof(this.groups[groupId]) == 'undefined') {
22503 return this.groups[groupId] ;
22516 * @class Roo.bootstrap.Radio
22517 * @extends Roo.bootstrap.Component
22518 * Bootstrap Radio class
22519 * @cfg {String} boxLabel - the label associated
22520 * @cfg {String} value - the value of radio
22523 * Create a new Radio
22524 * @param {Object} config The config object
22526 Roo.bootstrap.Radio = function(config){
22527 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22531 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22537 getAutoCreate : function()
22541 cls : 'form-group radio',
22546 html : this.boxLabel
22554 initEvents : function()
22556 this.parent().register(this);
22558 this.el.on('click', this.onClick, this);
22562 onClick : function(e)
22564 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22565 this.setChecked(true);
22569 setChecked : function(state, suppressEvent)
22571 this.parent().setValue(this.value, suppressEvent);
22575 setBoxLabel : function(v)
22580 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22595 * @class Roo.bootstrap.SecurePass
22596 * @extends Roo.bootstrap.Input
22597 * Bootstrap SecurePass class
22601 * Create a new SecurePass
22602 * @param {Object} config The config object
22605 Roo.bootstrap.SecurePass = function (config) {
22606 // these go here, so the translation tool can replace them..
22608 PwdEmpty: "Please type a password, and then retype it to confirm.",
22609 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22610 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22611 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22612 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22613 FNInPwd: "Your password can't contain your first name. Please type a different password.",
22614 LNInPwd: "Your password can't contain your last name. Please type a different password.",
22615 TooWeak: "Your password is Too Weak."
22617 this.meterLabel = "Password strength:";
22618 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22619 this.meterClass = [
22620 "roo-password-meter-tooweak",
22621 "roo-password-meter-weak",
22622 "roo-password-meter-medium",
22623 "roo-password-meter-strong",
22624 "roo-password-meter-grey"
22629 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22632 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22634 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22636 * PwdEmpty: "Please type a password, and then retype it to confirm.",
22637 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22638 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22639 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22640 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22641 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
22642 * LNInPwd: "Your password can't contain your last name. Please type a different password."
22652 * @cfg {String/Object} Label for the strength meter (defaults to
22653 * 'Password strength:')
22658 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22659 * ['Weak', 'Medium', 'Strong'])
22662 pwdStrengths: false,
22675 initEvents: function ()
22677 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22679 if (this.el.is('input[type=password]') && Roo.isSafari) {
22680 this.el.on('keydown', this.SafariOnKeyDown, this);
22683 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22686 onRender: function (ct, position)
22688 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22689 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22690 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22692 this.trigger.createChild({
22697 cls: 'roo-password-meter-grey col-xs-12',
22700 //width: this.meterWidth + 'px'
22704 cls: 'roo-password-meter-text'
22710 if (this.hideTrigger) {
22711 this.trigger.setDisplayed(false);
22713 this.setSize(this.width || '', this.height || '');
22716 onDestroy: function ()
22718 if (this.trigger) {
22719 this.trigger.removeAllListeners();
22720 this.trigger.remove();
22723 this.wrap.remove();
22725 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22728 checkStrength: function ()
22730 var pwd = this.inputEl().getValue();
22731 if (pwd == this._lastPwd) {
22736 if (this.ClientSideStrongPassword(pwd)) {
22738 } else if (this.ClientSideMediumPassword(pwd)) {
22740 } else if (this.ClientSideWeakPassword(pwd)) {
22746 Roo.log('strength1: ' + strength);
22748 //var pm = this.trigger.child('div/div/div').dom;
22749 var pm = this.trigger.child('div/div');
22750 pm.removeClass(this.meterClass);
22751 pm.addClass(this.meterClass[strength]);
22754 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22756 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22758 this._lastPwd = pwd;
22762 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22764 this._lastPwd = '';
22766 var pm = this.trigger.child('div/div');
22767 pm.removeClass(this.meterClass);
22768 pm.addClass('roo-password-meter-grey');
22771 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22774 this.inputEl().dom.type='password';
22777 validateValue: function (value)
22780 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22783 if (value.length == 0) {
22784 if (this.allowBlank) {
22785 this.clearInvalid();
22789 this.markInvalid(this.errors.PwdEmpty);
22790 this.errorMsg = this.errors.PwdEmpty;
22798 if ('[\x21-\x7e]*'.match(value)) {
22799 this.markInvalid(this.errors.PwdBadChar);
22800 this.errorMsg = this.errors.PwdBadChar;
22803 if (value.length < 6) {
22804 this.markInvalid(this.errors.PwdShort);
22805 this.errorMsg = this.errors.PwdShort;
22808 if (value.length > 16) {
22809 this.markInvalid(this.errors.PwdLong);
22810 this.errorMsg = this.errors.PwdLong;
22814 if (this.ClientSideStrongPassword(value)) {
22816 } else if (this.ClientSideMediumPassword(value)) {
22818 } else if (this.ClientSideWeakPassword(value)) {
22825 if (strength < 2) {
22826 //this.markInvalid(this.errors.TooWeak);
22827 this.errorMsg = this.errors.TooWeak;
22832 console.log('strength2: ' + strength);
22834 //var pm = this.trigger.child('div/div/div').dom;
22836 var pm = this.trigger.child('div/div');
22837 pm.removeClass(this.meterClass);
22838 pm.addClass(this.meterClass[strength]);
22840 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22842 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22844 this.errorMsg = '';
22848 CharacterSetChecks: function (type)
22851 this.fResult = false;
22854 isctype: function (character, type)
22857 case this.kCapitalLetter:
22858 if (character >= 'A' && character <= 'Z') {
22863 case this.kSmallLetter:
22864 if (character >= 'a' && character <= 'z') {
22870 if (character >= '0' && character <= '9') {
22875 case this.kPunctuation:
22876 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22887 IsLongEnough: function (pwd, size)
22889 return !(pwd == null || isNaN(size) || pwd.length < size);
22892 SpansEnoughCharacterSets: function (word, nb)
22894 if (!this.IsLongEnough(word, nb))
22899 var characterSetChecks = new Array(
22900 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22901 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22904 for (var index = 0; index < word.length; ++index) {
22905 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22906 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22907 characterSetChecks[nCharSet].fResult = true;
22914 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22915 if (characterSetChecks[nCharSet].fResult) {
22920 if (nCharSets < nb) {
22926 ClientSideStrongPassword: function (pwd)
22928 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22931 ClientSideMediumPassword: function (pwd)
22933 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22936 ClientSideWeakPassword: function (pwd)
22938 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22941 })//<script type="text/javascript">
22944 * Based Ext JS Library 1.1.1
22945 * Copyright(c) 2006-2007, Ext JS, LLC.
22951 * @class Roo.HtmlEditorCore
22952 * @extends Roo.Component
22953 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22955 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22958 Roo.HtmlEditorCore = function(config){
22961 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22966 * @event initialize
22967 * Fires when the editor is fully initialized (including the iframe)
22968 * @param {Roo.HtmlEditorCore} this
22973 * Fires when the editor is first receives the focus. Any insertion must wait
22974 * until after this event.
22975 * @param {Roo.HtmlEditorCore} this
22979 * @event beforesync
22980 * Fires before the textarea is updated with content from the editor iframe. Return false
22981 * to cancel the sync.
22982 * @param {Roo.HtmlEditorCore} this
22983 * @param {String} html
22987 * @event beforepush
22988 * Fires before the iframe editor is updated with content from the textarea. Return false
22989 * to cancel the push.
22990 * @param {Roo.HtmlEditorCore} this
22991 * @param {String} html
22996 * Fires when the textarea is updated with content from the editor iframe.
22997 * @param {Roo.HtmlEditorCore} this
22998 * @param {String} html
23003 * Fires when the iframe editor is updated with content from the textarea.
23004 * @param {Roo.HtmlEditorCore} this
23005 * @param {String} html
23010 * @event editorevent
23011 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23012 * @param {Roo.HtmlEditorCore} this
23018 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23020 // defaults : white / black...
23021 this.applyBlacklists();
23028 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23032 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23038 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23043 * @cfg {Number} height (in pixels)
23047 * @cfg {Number} width (in pixels)
23052 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23055 stylesheets: false,
23060 // private properties
23061 validationEvent : false,
23063 initialized : false,
23065 sourceEditMode : false,
23066 onFocus : Roo.emptyFn,
23068 hideMode:'offsets',
23072 // blacklist + whitelisted elements..
23079 * Protected method that will not generally be called directly. It
23080 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23081 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23083 getDocMarkup : function(){
23087 // inherit styels from page...??
23088 if (this.stylesheets === false) {
23090 Roo.get(document.head).select('style').each(function(node) {
23091 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23094 Roo.get(document.head).select('link').each(function(node) {
23095 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23098 } else if (!this.stylesheets.length) {
23100 st = '<style type="text/css">' +
23101 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23104 st = '<style type="text/css">' +
23109 st += '<style type="text/css">' +
23110 'IMG { cursor: pointer } ' +
23113 var cls = 'roo-htmleditor-body';
23115 if(this.bodyCls.length){
23116 cls += ' ' + this.bodyCls;
23119 return '<html><head>' + st +
23120 //<style type="text/css">' +
23121 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23123 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23127 onRender : function(ct, position)
23130 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23131 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23134 this.el.dom.style.border = '0 none';
23135 this.el.dom.setAttribute('tabIndex', -1);
23136 this.el.addClass('x-hidden hide');
23140 if(Roo.isIE){ // fix IE 1px bogus margin
23141 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23145 this.frameId = Roo.id();
23149 var iframe = this.owner.wrap.createChild({
23151 cls: 'form-control', // bootstrap..
23153 name: this.frameId,
23154 frameBorder : 'no',
23155 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23160 this.iframe = iframe.dom;
23162 this.assignDocWin();
23164 this.doc.designMode = 'on';
23167 this.doc.write(this.getDocMarkup());
23171 var task = { // must defer to wait for browser to be ready
23173 //console.log("run task?" + this.doc.readyState);
23174 this.assignDocWin();
23175 if(this.doc.body || this.doc.readyState == 'complete'){
23177 this.doc.designMode="on";
23181 Roo.TaskMgr.stop(task);
23182 this.initEditor.defer(10, this);
23189 Roo.TaskMgr.start(task);
23194 onResize : function(w, h)
23196 Roo.log('resize: ' +w + ',' + h );
23197 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23201 if(typeof w == 'number'){
23203 this.iframe.style.width = w + 'px';
23205 if(typeof h == 'number'){
23207 this.iframe.style.height = h + 'px';
23209 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23216 * Toggles the editor between standard and source edit mode.
23217 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23219 toggleSourceEdit : function(sourceEditMode){
23221 this.sourceEditMode = sourceEditMode === true;
23223 if(this.sourceEditMode){
23225 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23228 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23229 //this.iframe.className = '';
23232 //this.setSize(this.owner.wrap.getSize());
23233 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23240 * Protected method that will not generally be called directly. If you need/want
23241 * custom HTML cleanup, this is the method you should override.
23242 * @param {String} html The HTML to be cleaned
23243 * return {String} The cleaned HTML
23245 cleanHtml : function(html){
23246 html = String(html);
23247 if(html.length > 5){
23248 if(Roo.isSafari){ // strip safari nonsense
23249 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23252 if(html == ' '){
23259 * HTML Editor -> Textarea
23260 * Protected method that will not generally be called directly. Syncs the contents
23261 * of the editor iframe with the textarea.
23263 syncValue : function(){
23264 if(this.initialized){
23265 var bd = (this.doc.body || this.doc.documentElement);
23266 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23267 var html = bd.innerHTML;
23269 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23270 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23272 html = '<div style="'+m[0]+'">' + html + '</div>';
23275 html = this.cleanHtml(html);
23276 // fix up the special chars.. normaly like back quotes in word...
23277 // however we do not want to do this with chinese..
23278 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23280 var cc = match.charCodeAt();
23282 // Get the character value, handling surrogate pairs
23283 if (match.length == 2) {
23284 // It's a surrogate pair, calculate the Unicode code point
23285 var high = match.charCodeAt(0) - 0xD800;
23286 var low = match.charCodeAt(1) - 0xDC00;
23287 cc = (high * 0x400) + low + 0x10000;
23289 (cc >= 0x4E00 && cc < 0xA000 ) ||
23290 (cc >= 0x3400 && cc < 0x4E00 ) ||
23291 (cc >= 0xf900 && cc < 0xfb00 )
23296 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23297 return "&#" + cc + ";";
23304 if(this.owner.fireEvent('beforesync', this, html) !== false){
23305 this.el.dom.value = html;
23306 this.owner.fireEvent('sync', this, html);
23312 * Protected method that will not generally be called directly. Pushes the value of the textarea
23313 * into the iframe editor.
23315 pushValue : function(){
23316 if(this.initialized){
23317 var v = this.el.dom.value.trim();
23319 // if(v.length < 1){
23323 if(this.owner.fireEvent('beforepush', this, v) !== false){
23324 var d = (this.doc.body || this.doc.documentElement);
23326 this.cleanUpPaste();
23327 this.el.dom.value = d.innerHTML;
23328 this.owner.fireEvent('push', this, v);
23334 deferFocus : function(){
23335 this.focus.defer(10, this);
23339 focus : function(){
23340 if(this.win && !this.sourceEditMode){
23347 assignDocWin: function()
23349 var iframe = this.iframe;
23352 this.doc = iframe.contentWindow.document;
23353 this.win = iframe.contentWindow;
23355 // if (!Roo.get(this.frameId)) {
23358 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23359 // this.win = Roo.get(this.frameId).dom.contentWindow;
23361 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23365 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23366 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23371 initEditor : function(){
23372 //console.log("INIT EDITOR");
23373 this.assignDocWin();
23377 this.doc.designMode="on";
23379 this.doc.write(this.getDocMarkup());
23382 var dbody = (this.doc.body || this.doc.documentElement);
23383 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23384 // this copies styles from the containing element into thsi one..
23385 // not sure why we need all of this..
23386 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23388 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23389 //ss['background-attachment'] = 'fixed'; // w3c
23390 dbody.bgProperties = 'fixed'; // ie
23391 //Roo.DomHelper.applyStyles(dbody, ss);
23392 Roo.EventManager.on(this.doc, {
23393 //'mousedown': this.onEditorEvent,
23394 'mouseup': this.onEditorEvent,
23395 'dblclick': this.onEditorEvent,
23396 'click': this.onEditorEvent,
23397 'keyup': this.onEditorEvent,
23402 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23404 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23405 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23407 this.initialized = true;
23409 this.owner.fireEvent('initialize', this);
23414 onDestroy : function(){
23420 //for (var i =0; i < this.toolbars.length;i++) {
23421 // // fixme - ask toolbars for heights?
23422 // this.toolbars[i].onDestroy();
23425 //this.wrap.dom.innerHTML = '';
23426 //this.wrap.remove();
23431 onFirstFocus : function(){
23433 this.assignDocWin();
23436 this.activated = true;
23439 if(Roo.isGecko){ // prevent silly gecko errors
23441 var s = this.win.getSelection();
23442 if(!s.focusNode || s.focusNode.nodeType != 3){
23443 var r = s.getRangeAt(0);
23444 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23449 this.execCmd('useCSS', true);
23450 this.execCmd('styleWithCSS', false);
23453 this.owner.fireEvent('activate', this);
23457 adjustFont: function(btn){
23458 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23459 //if(Roo.isSafari){ // safari
23462 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23463 if(Roo.isSafari){ // safari
23464 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23465 v = (v < 10) ? 10 : v;
23466 v = (v > 48) ? 48 : v;
23467 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23472 v = Math.max(1, v+adjust);
23474 this.execCmd('FontSize', v );
23477 onEditorEvent : function(e)
23479 this.owner.fireEvent('editorevent', this, e);
23480 // this.updateToolbar();
23481 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23484 insertTag : function(tg)
23486 // could be a bit smarter... -> wrap the current selected tRoo..
23487 if (tg.toLowerCase() == 'span' ||
23488 tg.toLowerCase() == 'code' ||
23489 tg.toLowerCase() == 'sup' ||
23490 tg.toLowerCase() == 'sub'
23493 range = this.createRange(this.getSelection());
23494 var wrappingNode = this.doc.createElement(tg.toLowerCase());
23495 wrappingNode.appendChild(range.extractContents());
23496 range.insertNode(wrappingNode);
23503 this.execCmd("formatblock", tg);
23507 insertText : function(txt)
23511 var range = this.createRange();
23512 range.deleteContents();
23513 //alert(Sender.getAttribute('label'));
23515 range.insertNode(this.doc.createTextNode(txt));
23521 * Executes a Midas editor command on the editor document and performs necessary focus and
23522 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23523 * @param {String} cmd The Midas command
23524 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23526 relayCmd : function(cmd, value){
23528 this.execCmd(cmd, value);
23529 this.owner.fireEvent('editorevent', this);
23530 //this.updateToolbar();
23531 this.owner.deferFocus();
23535 * Executes a Midas editor command directly on the editor document.
23536 * For visual commands, you should use {@link #relayCmd} instead.
23537 * <b>This should only be called after the editor is initialized.</b>
23538 * @param {String} cmd The Midas command
23539 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23541 execCmd : function(cmd, value){
23542 this.doc.execCommand(cmd, false, value === undefined ? null : value);
23549 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23551 * @param {String} text | dom node..
23553 insertAtCursor : function(text)
23556 if(!this.activated){
23562 var r = this.doc.selection.createRange();
23573 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23577 // from jquery ui (MIT licenced)
23579 var win = this.win;
23581 if (win.getSelection && win.getSelection().getRangeAt) {
23582 range = win.getSelection().getRangeAt(0);
23583 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23584 range.insertNode(node);
23585 } else if (win.document.selection && win.document.selection.createRange) {
23586 // no firefox support
23587 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23588 win.document.selection.createRange().pasteHTML(txt);
23590 // no firefox support
23591 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23592 this.execCmd('InsertHTML', txt);
23601 mozKeyPress : function(e){
23603 var c = e.getCharCode(), cmd;
23606 c = String.fromCharCode(c).toLowerCase();
23620 this.cleanUpPaste.defer(100, this);
23628 e.preventDefault();
23636 fixKeys : function(){ // load time branching for fastest keydown performance
23638 return function(e){
23639 var k = e.getKey(), r;
23642 r = this.doc.selection.createRange();
23645 r.pasteHTML('    ');
23652 r = this.doc.selection.createRange();
23654 var target = r.parentElement();
23655 if(!target || target.tagName.toLowerCase() != 'li'){
23657 r.pasteHTML('<br />');
23663 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23664 this.cleanUpPaste.defer(100, this);
23670 }else if(Roo.isOpera){
23671 return function(e){
23672 var k = e.getKey();
23676 this.execCmd('InsertHTML','    ');
23679 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23680 this.cleanUpPaste.defer(100, this);
23685 }else if(Roo.isSafari){
23686 return function(e){
23687 var k = e.getKey();
23691 this.execCmd('InsertText','\t');
23695 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23696 this.cleanUpPaste.defer(100, this);
23704 getAllAncestors: function()
23706 var p = this.getSelectedNode();
23709 a.push(p); // push blank onto stack..
23710 p = this.getParentElement();
23714 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23718 a.push(this.doc.body);
23722 lastSelNode : false,
23725 getSelection : function()
23727 this.assignDocWin();
23728 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23731 getSelectedNode: function()
23733 // this may only work on Gecko!!!
23735 // should we cache this!!!!
23740 var range = this.createRange(this.getSelection()).cloneRange();
23743 var parent = range.parentElement();
23745 var testRange = range.duplicate();
23746 testRange.moveToElementText(parent);
23747 if (testRange.inRange(range)) {
23750 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23753 parent = parent.parentElement;
23758 // is ancestor a text element.
23759 var ac = range.commonAncestorContainer;
23760 if (ac.nodeType == 3) {
23761 ac = ac.parentNode;
23764 var ar = ac.childNodes;
23767 var other_nodes = [];
23768 var has_other_nodes = false;
23769 for (var i=0;i<ar.length;i++) {
23770 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23773 // fullly contained node.
23775 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23780 // probably selected..
23781 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23782 other_nodes.push(ar[i]);
23786 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23791 has_other_nodes = true;
23793 if (!nodes.length && other_nodes.length) {
23794 nodes= other_nodes;
23796 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23802 createRange: function(sel)
23804 // this has strange effects when using with
23805 // top toolbar - not sure if it's a great idea.
23806 //this.editor.contentWindow.focus();
23807 if (typeof sel != "undefined") {
23809 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23811 return this.doc.createRange();
23814 return this.doc.createRange();
23817 getParentElement: function()
23820 this.assignDocWin();
23821 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23823 var range = this.createRange(sel);
23826 var p = range.commonAncestorContainer;
23827 while (p.nodeType == 3) { // text node
23838 * Range intersection.. the hard stuff...
23842 * [ -- selected range --- ]
23846 * if end is before start or hits it. fail.
23847 * if start is after end or hits it fail.
23849 * if either hits (but other is outside. - then it's not
23855 // @see http://www.thismuchiknow.co.uk/?p=64.
23856 rangeIntersectsNode : function(range, node)
23858 var nodeRange = node.ownerDocument.createRange();
23860 nodeRange.selectNode(node);
23862 nodeRange.selectNodeContents(node);
23865 var rangeStartRange = range.cloneRange();
23866 rangeStartRange.collapse(true);
23868 var rangeEndRange = range.cloneRange();
23869 rangeEndRange.collapse(false);
23871 var nodeStartRange = nodeRange.cloneRange();
23872 nodeStartRange.collapse(true);
23874 var nodeEndRange = nodeRange.cloneRange();
23875 nodeEndRange.collapse(false);
23877 return rangeStartRange.compareBoundaryPoints(
23878 Range.START_TO_START, nodeEndRange) == -1 &&
23879 rangeEndRange.compareBoundaryPoints(
23880 Range.START_TO_START, nodeStartRange) == 1;
23884 rangeCompareNode : function(range, node)
23886 var nodeRange = node.ownerDocument.createRange();
23888 nodeRange.selectNode(node);
23890 nodeRange.selectNodeContents(node);
23894 range.collapse(true);
23896 nodeRange.collapse(true);
23898 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23899 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23901 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23903 var nodeIsBefore = ss == 1;
23904 var nodeIsAfter = ee == -1;
23906 if (nodeIsBefore && nodeIsAfter) {
23909 if (!nodeIsBefore && nodeIsAfter) {
23910 return 1; //right trailed.
23913 if (nodeIsBefore && !nodeIsAfter) {
23914 return 2; // left trailed.
23920 // private? - in a new class?
23921 cleanUpPaste : function()
23923 // cleans up the whole document..
23924 Roo.log('cleanuppaste');
23926 this.cleanUpChildren(this.doc.body);
23927 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23928 if (clean != this.doc.body.innerHTML) {
23929 this.doc.body.innerHTML = clean;
23934 cleanWordChars : function(input) {// change the chars to hex code
23935 var he = Roo.HtmlEditorCore;
23937 var output = input;
23938 Roo.each(he.swapCodes, function(sw) {
23939 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23941 output = output.replace(swapper, sw[1]);
23948 cleanUpChildren : function (n)
23950 if (!n.childNodes.length) {
23953 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23954 this.cleanUpChild(n.childNodes[i]);
23961 cleanUpChild : function (node)
23964 //console.log(node);
23965 if (node.nodeName == "#text") {
23966 // clean up silly Windows -- stuff?
23969 if (node.nodeName == "#comment") {
23970 node.parentNode.removeChild(node);
23971 // clean up silly Windows -- stuff?
23974 var lcname = node.tagName.toLowerCase();
23975 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23976 // whitelist of tags..
23978 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23980 node.parentNode.removeChild(node);
23985 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23987 // spans with no attributes - just remove them..
23988 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23989 remove_keep_children = true;
23992 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23993 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23995 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23996 // remove_keep_children = true;
23999 if (remove_keep_children) {
24000 this.cleanUpChildren(node);
24001 // inserts everything just before this node...
24002 while (node.childNodes.length) {
24003 var cn = node.childNodes[0];
24004 node.removeChild(cn);
24005 node.parentNode.insertBefore(cn, node);
24007 node.parentNode.removeChild(node);
24011 if (!node.attributes || !node.attributes.length) {
24016 this.cleanUpChildren(node);
24020 function cleanAttr(n,v)
24023 if (v.match(/^\./) || v.match(/^\//)) {
24026 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24029 if (v.match(/^#/)) {
24032 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24033 node.removeAttribute(n);
24037 var cwhite = this.cwhite;
24038 var cblack = this.cblack;
24040 function cleanStyle(n,v)
24042 if (v.match(/expression/)) { //XSS?? should we even bother..
24043 node.removeAttribute(n);
24047 var parts = v.split(/;/);
24050 Roo.each(parts, function(p) {
24051 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24055 var l = p.split(':').shift().replace(/\s+/g,'');
24056 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24058 if ( cwhite.length && cblack.indexOf(l) > -1) {
24059 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24060 //node.removeAttribute(n);
24064 // only allow 'c whitelisted system attributes'
24065 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24066 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24067 //node.removeAttribute(n);
24077 if (clean.length) {
24078 node.setAttribute(n, clean.join(';'));
24080 node.removeAttribute(n);
24086 for (var i = node.attributes.length-1; i > -1 ; i--) {
24087 var a = node.attributes[i];
24090 if (a.name.toLowerCase().substr(0,2)=='on') {
24091 node.removeAttribute(a.name);
24094 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24095 node.removeAttribute(a.name);
24098 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24099 cleanAttr(a.name,a.value); // fixme..
24102 if (a.name == 'style') {
24103 cleanStyle(a.name,a.value);
24106 /// clean up MS crap..
24107 // tecnically this should be a list of valid class'es..
24110 if (a.name == 'class') {
24111 if (a.value.match(/^Mso/)) {
24112 node.removeAttribute('class');
24115 if (a.value.match(/^body$/)) {
24116 node.removeAttribute('class');
24127 this.cleanUpChildren(node);
24133 * Clean up MS wordisms...
24135 cleanWord : function(node)
24138 this.cleanWord(this.doc.body);
24143 node.nodeName == 'SPAN' &&
24144 !node.hasAttributes() &&
24145 node.childNodes.length == 1 &&
24146 node.firstChild.nodeName == "#text"
24148 var textNode = node.firstChild;
24149 node.removeChild(textNode);
24150 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24151 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24153 node.parentNode.insertBefore(textNode, node);
24154 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24155 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24157 node.parentNode.removeChild(node);
24160 if (node.nodeName == "#text") {
24161 // clean up silly Windows -- stuff?
24164 if (node.nodeName == "#comment") {
24165 node.parentNode.removeChild(node);
24166 // clean up silly Windows -- stuff?
24170 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24171 node.parentNode.removeChild(node);
24174 //Roo.log(node.tagName);
24175 // remove - but keep children..
24176 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24177 //Roo.log('-- removed');
24178 while (node.childNodes.length) {
24179 var cn = node.childNodes[0];
24180 node.removeChild(cn);
24181 node.parentNode.insertBefore(cn, node);
24182 // move node to parent - and clean it..
24183 this.cleanWord(cn);
24185 node.parentNode.removeChild(node);
24186 /// no need to iterate chidlren = it's got none..
24187 //this.iterateChildren(node, this.cleanWord);
24191 if (node.className.length) {
24193 var cn = node.className.split(/\W+/);
24195 Roo.each(cn, function(cls) {
24196 if (cls.match(/Mso[a-zA-Z]+/)) {
24201 node.className = cna.length ? cna.join(' ') : '';
24203 node.removeAttribute("class");
24207 if (node.hasAttribute("lang")) {
24208 node.removeAttribute("lang");
24211 if (node.hasAttribute("style")) {
24213 var styles = node.getAttribute("style").split(";");
24215 Roo.each(styles, function(s) {
24216 if (!s.match(/:/)) {
24219 var kv = s.split(":");
24220 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24223 // what ever is left... we allow.
24226 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24227 if (!nstyle.length) {
24228 node.removeAttribute('style');
24231 this.iterateChildren(node, this.cleanWord);
24237 * iterateChildren of a Node, calling fn each time, using this as the scole..
24238 * @param {DomNode} node node to iterate children of.
24239 * @param {Function} fn method of this class to call on each item.
24241 iterateChildren : function(node, fn)
24243 if (!node.childNodes.length) {
24246 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24247 fn.call(this, node.childNodes[i])
24253 * cleanTableWidths.
24255 * Quite often pasting from word etc.. results in tables with column and widths.
24256 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24259 cleanTableWidths : function(node)
24264 this.cleanTableWidths(this.doc.body);
24269 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24272 Roo.log(node.tagName);
24273 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24274 this.iterateChildren(node, this.cleanTableWidths);
24277 if (node.hasAttribute('width')) {
24278 node.removeAttribute('width');
24282 if (node.hasAttribute("style")) {
24285 var styles = node.getAttribute("style").split(";");
24287 Roo.each(styles, function(s) {
24288 if (!s.match(/:/)) {
24291 var kv = s.split(":");
24292 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24295 // what ever is left... we allow.
24298 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24299 if (!nstyle.length) {
24300 node.removeAttribute('style');
24304 this.iterateChildren(node, this.cleanTableWidths);
24312 domToHTML : function(currentElement, depth, nopadtext) {
24314 depth = depth || 0;
24315 nopadtext = nopadtext || false;
24317 if (!currentElement) {
24318 return this.domToHTML(this.doc.body);
24321 //Roo.log(currentElement);
24323 var allText = false;
24324 var nodeName = currentElement.nodeName;
24325 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24327 if (nodeName == '#text') {
24329 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24334 if (nodeName != 'BODY') {
24337 // Prints the node tagName, such as <A>, <IMG>, etc
24340 for(i = 0; i < currentElement.attributes.length;i++) {
24342 var aname = currentElement.attributes.item(i).name;
24343 if (!currentElement.attributes.item(i).value.length) {
24346 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24349 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24358 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24361 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24366 // Traverse the tree
24368 var currentElementChild = currentElement.childNodes.item(i);
24369 var allText = true;
24370 var innerHTML = '';
24372 while (currentElementChild) {
24373 // Formatting code (indent the tree so it looks nice on the screen)
24374 var nopad = nopadtext;
24375 if (lastnode == 'SPAN') {
24379 if (currentElementChild.nodeName == '#text') {
24380 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24381 toadd = nopadtext ? toadd : toadd.trim();
24382 if (!nopad && toadd.length > 80) {
24383 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24385 innerHTML += toadd;
24388 currentElementChild = currentElement.childNodes.item(i);
24394 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
24396 // Recursively traverse the tree structure of the child node
24397 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
24398 lastnode = currentElementChild.nodeName;
24400 currentElementChild=currentElement.childNodes.item(i);
24406 // The remaining code is mostly for formatting the tree
24407 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
24412 ret+= "</"+tagName+">";
24418 applyBlacklists : function()
24420 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
24421 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
24425 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24426 if (b.indexOf(tag) > -1) {
24429 this.white.push(tag);
24433 Roo.each(w, function(tag) {
24434 if (b.indexOf(tag) > -1) {
24437 if (this.white.indexOf(tag) > -1) {
24440 this.white.push(tag);
24445 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24446 if (w.indexOf(tag) > -1) {
24449 this.black.push(tag);
24453 Roo.each(b, function(tag) {
24454 if (w.indexOf(tag) > -1) {
24457 if (this.black.indexOf(tag) > -1) {
24460 this.black.push(tag);
24465 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
24466 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
24470 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24471 if (b.indexOf(tag) > -1) {
24474 this.cwhite.push(tag);
24478 Roo.each(w, function(tag) {
24479 if (b.indexOf(tag) > -1) {
24482 if (this.cwhite.indexOf(tag) > -1) {
24485 this.cwhite.push(tag);
24490 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24491 if (w.indexOf(tag) > -1) {
24494 this.cblack.push(tag);
24498 Roo.each(b, function(tag) {
24499 if (w.indexOf(tag) > -1) {
24502 if (this.cblack.indexOf(tag) > -1) {
24505 this.cblack.push(tag);
24510 setStylesheets : function(stylesheets)
24512 if(typeof(stylesheets) == 'string'){
24513 Roo.get(this.iframe.contentDocument.head).createChild({
24515 rel : 'stylesheet',
24524 Roo.each(stylesheets, function(s) {
24529 Roo.get(_this.iframe.contentDocument.head).createChild({
24531 rel : 'stylesheet',
24540 removeStylesheets : function()
24544 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24549 setStyle : function(style)
24551 Roo.get(this.iframe.contentDocument.head).createChild({
24560 // hide stuff that is not compatible
24574 * @event specialkey
24578 * @cfg {String} fieldClass @hide
24581 * @cfg {String} focusClass @hide
24584 * @cfg {String} autoCreate @hide
24587 * @cfg {String} inputType @hide
24590 * @cfg {String} invalidClass @hide
24593 * @cfg {String} invalidText @hide
24596 * @cfg {String} msgFx @hide
24599 * @cfg {String} validateOnBlur @hide
24603 Roo.HtmlEditorCore.white = [
24604 'area', 'br', 'img', 'input', 'hr', 'wbr',
24606 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
24607 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
24608 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
24609 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
24610 'table', 'ul', 'xmp',
24612 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
24615 'dir', 'menu', 'ol', 'ul', 'dl',
24621 Roo.HtmlEditorCore.black = [
24622 // 'embed', 'object', // enable - backend responsiblity to clean thiese
24624 'base', 'basefont', 'bgsound', 'blink', 'body',
24625 'frame', 'frameset', 'head', 'html', 'ilayer',
24626 'iframe', 'layer', 'link', 'meta', 'object',
24627 'script', 'style' ,'title', 'xml' // clean later..
24629 Roo.HtmlEditorCore.clean = [
24630 'script', 'style', 'title', 'xml'
24632 Roo.HtmlEditorCore.remove = [
24637 Roo.HtmlEditorCore.ablack = [
24641 Roo.HtmlEditorCore.aclean = [
24642 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24646 Roo.HtmlEditorCore.pwhite= [
24647 'http', 'https', 'mailto'
24650 // white listed style attributes.
24651 Roo.HtmlEditorCore.cwhite= [
24652 // 'text-align', /// default is to allow most things..
24658 // black listed style attributes.
24659 Roo.HtmlEditorCore.cblack= [
24660 // 'font-size' -- this can be set by the project
24664 Roo.HtmlEditorCore.swapCodes =[
24683 * @class Roo.bootstrap.HtmlEditor
24684 * @extends Roo.bootstrap.TextArea
24685 * Bootstrap HtmlEditor class
24688 * Create a new HtmlEditor
24689 * @param {Object} config The config object
24692 Roo.bootstrap.HtmlEditor = function(config){
24693 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24694 if (!this.toolbars) {
24695 this.toolbars = [];
24698 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24701 * @event initialize
24702 * Fires when the editor is fully initialized (including the iframe)
24703 * @param {HtmlEditor} this
24708 * Fires when the editor is first receives the focus. Any insertion must wait
24709 * until after this event.
24710 * @param {HtmlEditor} this
24714 * @event beforesync
24715 * Fires before the textarea is updated with content from the editor iframe. Return false
24716 * to cancel the sync.
24717 * @param {HtmlEditor} this
24718 * @param {String} html
24722 * @event beforepush
24723 * Fires before the iframe editor is updated with content from the textarea. Return false
24724 * to cancel the push.
24725 * @param {HtmlEditor} this
24726 * @param {String} html
24731 * Fires when the textarea is updated with content from the editor iframe.
24732 * @param {HtmlEditor} this
24733 * @param {String} html
24738 * Fires when the iframe editor is updated with content from the textarea.
24739 * @param {HtmlEditor} this
24740 * @param {String} html
24744 * @event editmodechange
24745 * Fires when the editor switches edit modes
24746 * @param {HtmlEditor} this
24747 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24749 editmodechange: true,
24751 * @event editorevent
24752 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24753 * @param {HtmlEditor} this
24757 * @event firstfocus
24758 * Fires when on first focus - needed by toolbars..
24759 * @param {HtmlEditor} this
24764 * Auto save the htmlEditor value as a file into Events
24765 * @param {HtmlEditor} this
24769 * @event savedpreview
24770 * preview the saved version of htmlEditor
24771 * @param {HtmlEditor} this
24778 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24782 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24787 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24792 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24797 * @cfg {Number} height (in pixels)
24801 * @cfg {Number} width (in pixels)
24806 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24809 stylesheets: false,
24814 // private properties
24815 validationEvent : false,
24817 initialized : false,
24820 onFocus : Roo.emptyFn,
24822 hideMode:'offsets',
24824 tbContainer : false,
24828 toolbarContainer :function() {
24829 return this.wrap.select('.x-html-editor-tb',true).first();
24833 * Protected method that will not generally be called directly. It
24834 * is called when the editor creates its toolbar. Override this method if you need to
24835 * add custom toolbar buttons.
24836 * @param {HtmlEditor} editor
24838 createToolbar : function(){
24839 Roo.log('renewing');
24840 Roo.log("create toolbars");
24842 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24843 this.toolbars[0].render(this.toolbarContainer());
24847 // if (!editor.toolbars || !editor.toolbars.length) {
24848 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24851 // for (var i =0 ; i < editor.toolbars.length;i++) {
24852 // editor.toolbars[i] = Roo.factory(
24853 // typeof(editor.toolbars[i]) == 'string' ?
24854 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24855 // Roo.bootstrap.HtmlEditor);
24856 // editor.toolbars[i].init(editor);
24862 onRender : function(ct, position)
24864 // Roo.log("Call onRender: " + this.xtype);
24866 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24868 this.wrap = this.inputEl().wrap({
24869 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24872 this.editorcore.onRender(ct, position);
24874 if (this.resizable) {
24875 this.resizeEl = new Roo.Resizable(this.wrap, {
24879 minHeight : this.height,
24880 height: this.height,
24881 handles : this.resizable,
24884 resize : function(r, w, h) {
24885 _t.onResize(w,h); // -something
24891 this.createToolbar(this);
24894 if(!this.width && this.resizable){
24895 this.setSize(this.wrap.getSize());
24897 if (this.resizeEl) {
24898 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24899 // should trigger onReize..
24905 onResize : function(w, h)
24907 Roo.log('resize: ' +w + ',' + h );
24908 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24912 if(this.inputEl() ){
24913 if(typeof w == 'number'){
24914 var aw = w - this.wrap.getFrameWidth('lr');
24915 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24918 if(typeof h == 'number'){
24919 var tbh = -11; // fixme it needs to tool bar size!
24920 for (var i =0; i < this.toolbars.length;i++) {
24921 // fixme - ask toolbars for heights?
24922 tbh += this.toolbars[i].el.getHeight();
24923 //if (this.toolbars[i].footer) {
24924 // tbh += this.toolbars[i].footer.el.getHeight();
24932 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24933 ah -= 5; // knock a few pixes off for look..
24934 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24938 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24939 this.editorcore.onResize(ew,eh);
24944 * Toggles the editor between standard and source edit mode.
24945 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24947 toggleSourceEdit : function(sourceEditMode)
24949 this.editorcore.toggleSourceEdit(sourceEditMode);
24951 if(this.editorcore.sourceEditMode){
24952 Roo.log('editor - showing textarea');
24955 // Roo.log(this.syncValue());
24957 this.inputEl().removeClass(['hide', 'x-hidden']);
24958 this.inputEl().dom.removeAttribute('tabIndex');
24959 this.inputEl().focus();
24961 Roo.log('editor - hiding textarea');
24963 // Roo.log(this.pushValue());
24966 this.inputEl().addClass(['hide', 'x-hidden']);
24967 this.inputEl().dom.setAttribute('tabIndex', -1);
24968 //this.deferFocus();
24971 if(this.resizable){
24972 this.setSize(this.wrap.getSize());
24975 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24978 // private (for BoxComponent)
24979 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24981 // private (for BoxComponent)
24982 getResizeEl : function(){
24986 // private (for BoxComponent)
24987 getPositionEl : function(){
24992 initEvents : function(){
24993 this.originalValue = this.getValue();
24997 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25000 // markInvalid : Roo.emptyFn,
25002 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25005 // clearInvalid : Roo.emptyFn,
25007 setValue : function(v){
25008 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25009 this.editorcore.pushValue();
25014 deferFocus : function(){
25015 this.focus.defer(10, this);
25019 focus : function(){
25020 this.editorcore.focus();
25026 onDestroy : function(){
25032 for (var i =0; i < this.toolbars.length;i++) {
25033 // fixme - ask toolbars for heights?
25034 this.toolbars[i].onDestroy();
25037 this.wrap.dom.innerHTML = '';
25038 this.wrap.remove();
25043 onFirstFocus : function(){
25044 //Roo.log("onFirstFocus");
25045 this.editorcore.onFirstFocus();
25046 for (var i =0; i < this.toolbars.length;i++) {
25047 this.toolbars[i].onFirstFocus();
25053 syncValue : function()
25055 this.editorcore.syncValue();
25058 pushValue : function()
25060 this.editorcore.pushValue();
25064 // hide stuff that is not compatible
25078 * @event specialkey
25082 * @cfg {String} fieldClass @hide
25085 * @cfg {String} focusClass @hide
25088 * @cfg {String} autoCreate @hide
25091 * @cfg {String} inputType @hide
25095 * @cfg {String} invalidText @hide
25098 * @cfg {String} msgFx @hide
25101 * @cfg {String} validateOnBlur @hide
25110 Roo.namespace('Roo.bootstrap.htmleditor');
25112 * @class Roo.bootstrap.HtmlEditorToolbar1
25118 new Roo.bootstrap.HtmlEditor({
25121 new Roo.bootstrap.HtmlEditorToolbar1({
25122 disable : { fonts: 1 , format: 1, ..., ... , ...],
25128 * @cfg {Object} disable List of elements to disable..
25129 * @cfg {Array} btns List of additional buttons.
25133 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25136 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25139 Roo.apply(this, config);
25141 // default disabled, based on 'good practice'..
25142 this.disable = this.disable || {};
25143 Roo.applyIf(this.disable, {
25146 specialElements : true
25148 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25150 this.editor = config.editor;
25151 this.editorcore = config.editor.editorcore;
25153 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25155 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25156 // dont call parent... till later.
25158 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25163 editorcore : false,
25168 "h1","h2","h3","h4","h5","h6",
25170 "abbr", "acronym", "address", "cite", "samp", "var",
25174 onRender : function(ct, position)
25176 // Roo.log("Call onRender: " + this.xtype);
25178 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25180 this.el.dom.style.marginBottom = '0';
25182 var editorcore = this.editorcore;
25183 var editor= this.editor;
25186 var btn = function(id,cmd , toggle, handler, html){
25188 var event = toggle ? 'toggle' : 'click';
25193 xns: Roo.bootstrap,
25197 enableToggle:toggle !== false,
25199 pressed : toggle ? false : null,
25202 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25203 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25209 // var cb_box = function...
25214 xns: Roo.bootstrap,
25219 xns: Roo.bootstrap,
25223 Roo.each(this.formats, function(f) {
25224 style.menu.items.push({
25226 xns: Roo.bootstrap,
25227 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25232 editorcore.insertTag(this.tagname);
25239 children.push(style);
25241 btn('bold',false,true);
25242 btn('italic',false,true);
25243 btn('align-left', 'justifyleft',true);
25244 btn('align-center', 'justifycenter',true);
25245 btn('align-right' , 'justifyright',true);
25246 btn('link', false, false, function(btn) {
25247 //Roo.log("create link?");
25248 var url = prompt(this.createLinkText, this.defaultLinkValue);
25249 if(url && url != 'http:/'+'/'){
25250 this.editorcore.relayCmd('createlink', url);
25253 btn('list','insertunorderedlist',true);
25254 btn('pencil', false,true, function(btn){
25256 this.toggleSourceEdit(btn.pressed);
25259 if (this.editor.btns.length > 0) {
25260 for (var i = 0; i<this.editor.btns.length; i++) {
25261 children.push(this.editor.btns[i]);
25269 xns: Roo.bootstrap,
25274 xns: Roo.bootstrap,
25279 cog.menu.items.push({
25281 xns: Roo.bootstrap,
25282 html : Clean styles,
25287 editorcore.insertTag(this.tagname);
25296 this.xtype = 'NavSimplebar';
25298 for(var i=0;i< children.length;i++) {
25300 this.buttons.add(this.addxtypeChild(children[i]));
25304 editor.on('editorevent', this.updateToolbar, this);
25306 onBtnClick : function(id)
25308 this.editorcore.relayCmd(id);
25309 this.editorcore.focus();
25313 * Protected method that will not generally be called directly. It triggers
25314 * a toolbar update by reading the markup state of the current selection in the editor.
25316 updateToolbar: function(){
25318 if(!this.editorcore.activated){
25319 this.editor.onFirstFocus(); // is this neeed?
25323 var btns = this.buttons;
25324 var doc = this.editorcore.doc;
25325 btns.get('bold').setActive(doc.queryCommandState('bold'));
25326 btns.get('italic').setActive(doc.queryCommandState('italic'));
25327 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25329 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25330 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25331 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25333 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25334 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25337 var ans = this.editorcore.getAllAncestors();
25338 if (this.formatCombo) {
25341 var store = this.formatCombo.store;
25342 this.formatCombo.setValue("");
25343 for (var i =0; i < ans.length;i++) {
25344 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25346 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25354 // hides menus... - so this cant be on a menu...
25355 Roo.bootstrap.MenuMgr.hideAll();
25357 Roo.bootstrap.MenuMgr.hideAll();
25358 //this.editorsyncValue();
25360 onFirstFocus: function() {
25361 this.buttons.each(function(item){
25365 toggleSourceEdit : function(sourceEditMode){
25368 if(sourceEditMode){
25369 Roo.log("disabling buttons");
25370 this.buttons.each( function(item){
25371 if(item.cmd != 'pencil'){
25377 Roo.log("enabling buttons");
25378 if(this.editorcore.initialized){
25379 this.buttons.each( function(item){
25385 Roo.log("calling toggole on editor");
25386 // tell the editor that it's been pressed..
25387 this.editor.toggleSourceEdit(sourceEditMode);
25397 * @class Roo.bootstrap.Table.AbstractSelectionModel
25398 * @extends Roo.util.Observable
25399 * Abstract base class for grid SelectionModels. It provides the interface that should be
25400 * implemented by descendant classes. This class should not be directly instantiated.
25403 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25404 this.locked = false;
25405 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25409 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
25410 /** @ignore Called by the grid automatically. Do not call directly. */
25411 init : function(grid){
25417 * Locks the selections.
25420 this.locked = true;
25424 * Unlocks the selections.
25426 unlock : function(){
25427 this.locked = false;
25431 * Returns true if the selections are locked.
25432 * @return {Boolean}
25434 isLocked : function(){
25435 return this.locked;
25439 initEvents : function ()
25445 * @extends Roo.bootstrap.Table.AbstractSelectionModel
25446 * @class Roo.bootstrap.Table.RowSelectionModel
25447 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25448 * It supports multiple selections and keyboard selection/navigation.
25450 * @param {Object} config
25453 Roo.bootstrap.Table.RowSelectionModel = function(config){
25454 Roo.apply(this, config);
25455 this.selections = new Roo.util.MixedCollection(false, function(o){
25460 this.lastActive = false;
25464 * @event selectionchange
25465 * Fires when the selection changes
25466 * @param {SelectionModel} this
25468 "selectionchange" : true,
25470 * @event afterselectionchange
25471 * Fires after the selection changes (eg. by key press or clicking)
25472 * @param {SelectionModel} this
25474 "afterselectionchange" : true,
25476 * @event beforerowselect
25477 * Fires when a row is selected being selected, return false to cancel.
25478 * @param {SelectionModel} this
25479 * @param {Number} rowIndex The selected index
25480 * @param {Boolean} keepExisting False if other selections will be cleared
25482 "beforerowselect" : true,
25485 * Fires when a row is selected.
25486 * @param {SelectionModel} this
25487 * @param {Number} rowIndex The selected index
25488 * @param {Roo.data.Record} r The record
25490 "rowselect" : true,
25492 * @event rowdeselect
25493 * Fires when a row is deselected.
25494 * @param {SelectionModel} this
25495 * @param {Number} rowIndex The selected index
25497 "rowdeselect" : true
25499 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25500 this.locked = false;
25503 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
25505 * @cfg {Boolean} singleSelect
25506 * True to allow selection of only one row at a time (defaults to false)
25508 singleSelect : false,
25511 initEvents : function()
25514 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25515 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
25516 //}else{ // allow click to work like normal
25517 // this.grid.on("rowclick", this.handleDragableRowClick, this);
25519 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25520 this.grid.on("rowclick", this.handleMouseDown, this);
25522 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25523 "up" : function(e){
25525 this.selectPrevious(e.shiftKey);
25526 }else if(this.last !== false && this.lastActive !== false){
25527 var last = this.last;
25528 this.selectRange(this.last, this.lastActive-1);
25529 this.grid.getView().focusRow(this.lastActive);
25530 if(last !== false){
25534 this.selectFirstRow();
25536 this.fireEvent("afterselectionchange", this);
25538 "down" : function(e){
25540 this.selectNext(e.shiftKey);
25541 }else if(this.last !== false && this.lastActive !== false){
25542 var last = this.last;
25543 this.selectRange(this.last, this.lastActive+1);
25544 this.grid.getView().focusRow(this.lastActive);
25545 if(last !== false){
25549 this.selectFirstRow();
25551 this.fireEvent("afterselectionchange", this);
25555 this.grid.store.on('load', function(){
25556 this.selections.clear();
25559 var view = this.grid.view;
25560 view.on("refresh", this.onRefresh, this);
25561 view.on("rowupdated", this.onRowUpdated, this);
25562 view.on("rowremoved", this.onRemove, this);
25567 onRefresh : function()
25569 var ds = this.grid.store, i, v = this.grid.view;
25570 var s = this.selections;
25571 s.each(function(r){
25572 if((i = ds.indexOfId(r.id)) != -1){
25581 onRemove : function(v, index, r){
25582 this.selections.remove(r);
25586 onRowUpdated : function(v, index, r){
25587 if(this.isSelected(r)){
25588 v.onRowSelect(index);
25594 * @param {Array} records The records to select
25595 * @param {Boolean} keepExisting (optional) True to keep existing selections
25597 selectRecords : function(records, keepExisting)
25600 this.clearSelections();
25602 var ds = this.grid.store;
25603 for(var i = 0, len = records.length; i < len; i++){
25604 this.selectRow(ds.indexOf(records[i]), true);
25609 * Gets the number of selected rows.
25612 getCount : function(){
25613 return this.selections.length;
25617 * Selects the first row in the grid.
25619 selectFirstRow : function(){
25624 * Select the last row.
25625 * @param {Boolean} keepExisting (optional) True to keep existing selections
25627 selectLastRow : function(keepExisting){
25628 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25629 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25633 * Selects the row immediately following the last selected row.
25634 * @param {Boolean} keepExisting (optional) True to keep existing selections
25636 selectNext : function(keepExisting)
25638 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25639 this.selectRow(this.last+1, keepExisting);
25640 this.grid.getView().focusRow(this.last);
25645 * Selects the row that precedes the last selected row.
25646 * @param {Boolean} keepExisting (optional) True to keep existing selections
25648 selectPrevious : function(keepExisting){
25650 this.selectRow(this.last-1, keepExisting);
25651 this.grid.getView().focusRow(this.last);
25656 * Returns the selected records
25657 * @return {Array} Array of selected records
25659 getSelections : function(){
25660 return [].concat(this.selections.items);
25664 * Returns the first selected record.
25667 getSelected : function(){
25668 return this.selections.itemAt(0);
25673 * Clears all selections.
25675 clearSelections : function(fast)
25681 var ds = this.grid.store;
25682 var s = this.selections;
25683 s.each(function(r){
25684 this.deselectRow(ds.indexOfId(r.id));
25688 this.selections.clear();
25695 * Selects all rows.
25697 selectAll : function(){
25701 this.selections.clear();
25702 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25703 this.selectRow(i, true);
25708 * Returns True if there is a selection.
25709 * @return {Boolean}
25711 hasSelection : function(){
25712 return this.selections.length > 0;
25716 * Returns True if the specified row is selected.
25717 * @param {Number/Record} record The record or index of the record to check
25718 * @return {Boolean}
25720 isSelected : function(index){
25721 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25722 return (r && this.selections.key(r.id) ? true : false);
25726 * Returns True if the specified record id is selected.
25727 * @param {String} id The id of record to check
25728 * @return {Boolean}
25730 isIdSelected : function(id){
25731 return (this.selections.key(id) ? true : false);
25736 handleMouseDBClick : function(e, t){
25740 handleMouseDown : function(e, t)
25742 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25743 if(this.isLocked() || rowIndex < 0 ){
25746 if(e.shiftKey && this.last !== false){
25747 var last = this.last;
25748 this.selectRange(last, rowIndex, e.ctrlKey);
25749 this.last = last; // reset the last
25753 var isSelected = this.isSelected(rowIndex);
25754 //Roo.log("select row:" + rowIndex);
25756 this.deselectRow(rowIndex);
25758 this.selectRow(rowIndex, true);
25762 if(e.button !== 0 && isSelected){
25763 alert('rowIndex 2: ' + rowIndex);
25764 view.focusRow(rowIndex);
25765 }else if(e.ctrlKey && isSelected){
25766 this.deselectRow(rowIndex);
25767 }else if(!isSelected){
25768 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25769 view.focusRow(rowIndex);
25773 this.fireEvent("afterselectionchange", this);
25776 handleDragableRowClick : function(grid, rowIndex, e)
25778 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25779 this.selectRow(rowIndex, false);
25780 grid.view.focusRow(rowIndex);
25781 this.fireEvent("afterselectionchange", this);
25786 * Selects multiple rows.
25787 * @param {Array} rows Array of the indexes of the row to select
25788 * @param {Boolean} keepExisting (optional) True to keep existing selections
25790 selectRows : function(rows, keepExisting){
25792 this.clearSelections();
25794 for(var i = 0, len = rows.length; i < len; i++){
25795 this.selectRow(rows[i], true);
25800 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25801 * @param {Number} startRow The index of the first row in the range
25802 * @param {Number} endRow The index of the last row in the range
25803 * @param {Boolean} keepExisting (optional) True to retain existing selections
25805 selectRange : function(startRow, endRow, keepExisting){
25810 this.clearSelections();
25812 if(startRow <= endRow){
25813 for(var i = startRow; i <= endRow; i++){
25814 this.selectRow(i, true);
25817 for(var i = startRow; i >= endRow; i--){
25818 this.selectRow(i, true);
25824 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25825 * @param {Number} startRow The index of the first row in the range
25826 * @param {Number} endRow The index of the last row in the range
25828 deselectRange : function(startRow, endRow, preventViewNotify){
25832 for(var i = startRow; i <= endRow; i++){
25833 this.deselectRow(i, preventViewNotify);
25839 * @param {Number} row The index of the row to select
25840 * @param {Boolean} keepExisting (optional) True to keep existing selections
25842 selectRow : function(index, keepExisting, preventViewNotify)
25844 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25847 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25848 if(!keepExisting || this.singleSelect){
25849 this.clearSelections();
25852 var r = this.grid.store.getAt(index);
25853 //console.log('selectRow - record id :' + r.id);
25855 this.selections.add(r);
25856 this.last = this.lastActive = index;
25857 if(!preventViewNotify){
25858 var proxy = new Roo.Element(
25859 this.grid.getRowDom(index)
25861 proxy.addClass('bg-info info');
25863 this.fireEvent("rowselect", this, index, r);
25864 this.fireEvent("selectionchange", this);
25870 * @param {Number} row The index of the row to deselect
25872 deselectRow : function(index, preventViewNotify)
25877 if(this.last == index){
25880 if(this.lastActive == index){
25881 this.lastActive = false;
25884 var r = this.grid.store.getAt(index);
25889 this.selections.remove(r);
25890 //.console.log('deselectRow - record id :' + r.id);
25891 if(!preventViewNotify){
25893 var proxy = new Roo.Element(
25894 this.grid.getRowDom(index)
25896 proxy.removeClass('bg-info info');
25898 this.fireEvent("rowdeselect", this, index);
25899 this.fireEvent("selectionchange", this);
25903 restoreLast : function(){
25905 this.last = this._last;
25910 acceptsNav : function(row, col, cm){
25911 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25915 onEditorKey : function(field, e){
25916 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25921 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25923 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25925 }else if(k == e.ENTER && !e.ctrlKey){
25929 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25931 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25933 }else if(k == e.ESC){
25937 g.startEditing(newCell[0], newCell[1]);
25943 * Ext JS Library 1.1.1
25944 * Copyright(c) 2006-2007, Ext JS, LLC.
25946 * Originally Released Under LGPL - original licence link has changed is not relivant.
25949 * <script type="text/javascript">
25953 * @class Roo.bootstrap.PagingToolbar
25954 * @extends Roo.bootstrap.NavSimplebar
25955 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25957 * Create a new PagingToolbar
25958 * @param {Object} config The config object
25959 * @param {Roo.data.Store} store
25961 Roo.bootstrap.PagingToolbar = function(config)
25963 // old args format still supported... - xtype is prefered..
25964 // created from xtype...
25966 this.ds = config.dataSource;
25968 if (config.store && !this.ds) {
25969 this.store= Roo.factory(config.store, Roo.data);
25970 this.ds = this.store;
25971 this.ds.xmodule = this.xmodule || false;
25974 this.toolbarItems = [];
25975 if (config.items) {
25976 this.toolbarItems = config.items;
25979 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25984 this.bind(this.ds);
25987 if (Roo.bootstrap.version == 4) {
25988 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25990 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25995 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25997 * @cfg {Roo.data.Store} dataSource
25998 * The underlying data store providing the paged data
26001 * @cfg {String/HTMLElement/Element} container
26002 * container The id or element that will contain the toolbar
26005 * @cfg {Boolean} displayInfo
26006 * True to display the displayMsg (defaults to false)
26009 * @cfg {Number} pageSize
26010 * The number of records to display per page (defaults to 20)
26014 * @cfg {String} displayMsg
26015 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26017 displayMsg : 'Displaying {0} - {1} of {2}',
26019 * @cfg {String} emptyMsg
26020 * The message to display when no records are found (defaults to "No data to display")
26022 emptyMsg : 'No data to display',
26024 * Customizable piece of the default paging text (defaults to "Page")
26027 beforePageText : "Page",
26029 * Customizable piece of the default paging text (defaults to "of %0")
26032 afterPageText : "of {0}",
26034 * Customizable piece of the default paging text (defaults to "First Page")
26037 firstText : "First Page",
26039 * Customizable piece of the default paging text (defaults to "Previous Page")
26042 prevText : "Previous Page",
26044 * Customizable piece of the default paging text (defaults to "Next Page")
26047 nextText : "Next Page",
26049 * Customizable piece of the default paging text (defaults to "Last Page")
26052 lastText : "Last Page",
26054 * Customizable piece of the default paging text (defaults to "Refresh")
26057 refreshText : "Refresh",
26061 onRender : function(ct, position)
26063 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26064 this.navgroup.parentId = this.id;
26065 this.navgroup.onRender(this.el, null);
26066 // add the buttons to the navgroup
26068 if(this.displayInfo){
26069 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26070 this.displayEl = this.el.select('.x-paging-info', true).first();
26071 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26072 // this.displayEl = navel.el.select('span',true).first();
26078 Roo.each(_this.buttons, function(e){ // this might need to use render????
26079 Roo.factory(e).render(_this.el);
26083 Roo.each(_this.toolbarItems, function(e) {
26084 _this.navgroup.addItem(e);
26088 this.first = this.navgroup.addItem({
26089 tooltip: this.firstText,
26090 cls: "prev btn-outline-secondary",
26091 html : ' <i class="fa fa-step-backward"></i>',
26093 preventDefault: true,
26094 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26097 this.prev = this.navgroup.addItem({
26098 tooltip: this.prevText,
26099 cls: "prev btn-outline-secondary",
26100 html : ' <i class="fa fa-backward"></i>',
26102 preventDefault: true,
26103 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26105 //this.addSeparator();
26108 var field = this.navgroup.addItem( {
26110 cls : 'x-paging-position btn-outline-secondary',
26112 html : this.beforePageText +
26113 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26114 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26117 this.field = field.el.select('input', true).first();
26118 this.field.on("keydown", this.onPagingKeydown, this);
26119 this.field.on("focus", function(){this.dom.select();});
26122 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26123 //this.field.setHeight(18);
26124 //this.addSeparator();
26125 this.next = this.navgroup.addItem({
26126 tooltip: this.nextText,
26127 cls: "next btn-outline-secondary",
26128 html : ' <i class="fa fa-forward"></i>',
26130 preventDefault: true,
26131 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26133 this.last = this.navgroup.addItem({
26134 tooltip: this.lastText,
26135 html : ' <i class="fa fa-step-forward"></i>',
26136 cls: "next btn-outline-secondary",
26138 preventDefault: true,
26139 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26141 //this.addSeparator();
26142 this.loading = this.navgroup.addItem({
26143 tooltip: this.refreshText,
26144 cls: "btn-outline-secondary",
26145 html : ' <i class="fa fa-refresh"></i>',
26146 preventDefault: true,
26147 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26153 updateInfo : function(){
26154 if(this.displayEl){
26155 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26156 var msg = count == 0 ?
26160 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26162 this.displayEl.update(msg);
26167 onLoad : function(ds, r, o)
26169 this.cursor = o.params.start ? o.params.start : 0;
26171 var d = this.getPageData(),
26176 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26177 this.field.dom.value = ap;
26178 this.first.setDisabled(ap == 1);
26179 this.prev.setDisabled(ap == 1);
26180 this.next.setDisabled(ap == ps);
26181 this.last.setDisabled(ap == ps);
26182 this.loading.enable();
26187 getPageData : function(){
26188 var total = this.ds.getTotalCount();
26191 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26192 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26197 onLoadError : function(){
26198 this.loading.enable();
26202 onPagingKeydown : function(e){
26203 var k = e.getKey();
26204 var d = this.getPageData();
26206 var v = this.field.dom.value, pageNum;
26207 if(!v || isNaN(pageNum = parseInt(v, 10))){
26208 this.field.dom.value = d.activePage;
26211 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26212 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26215 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))
26217 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26218 this.field.dom.value = pageNum;
26219 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26222 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26224 var v = this.field.dom.value, pageNum;
26225 var increment = (e.shiftKey) ? 10 : 1;
26226 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26229 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26230 this.field.dom.value = d.activePage;
26233 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26235 this.field.dom.value = parseInt(v, 10) + increment;
26236 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26237 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26244 beforeLoad : function(){
26246 this.loading.disable();
26251 onClick : function(which){
26260 ds.load({params:{start: 0, limit: this.pageSize}});
26263 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26266 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26269 var total = ds.getTotalCount();
26270 var extra = total % this.pageSize;
26271 var lastStart = extra ? (total - extra) : total-this.pageSize;
26272 ds.load({params:{start: lastStart, limit: this.pageSize}});
26275 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26281 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26282 * @param {Roo.data.Store} store The data store to unbind
26284 unbind : function(ds){
26285 ds.un("beforeload", this.beforeLoad, this);
26286 ds.un("load", this.onLoad, this);
26287 ds.un("loadexception", this.onLoadError, this);
26288 ds.un("remove", this.updateInfo, this);
26289 ds.un("add", this.updateInfo, this);
26290 this.ds = undefined;
26294 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26295 * @param {Roo.data.Store} store The data store to bind
26297 bind : function(ds){
26298 ds.on("beforeload", this.beforeLoad, this);
26299 ds.on("load", this.onLoad, this);
26300 ds.on("loadexception", this.onLoadError, this);
26301 ds.on("remove", this.updateInfo, this);
26302 ds.on("add", this.updateInfo, this);
26313 * @class Roo.bootstrap.MessageBar
26314 * @extends Roo.bootstrap.Component
26315 * Bootstrap MessageBar class
26316 * @cfg {String} html contents of the MessageBar
26317 * @cfg {String} weight (info | success | warning | danger) default info
26318 * @cfg {String} beforeClass insert the bar before the given class
26319 * @cfg {Boolean} closable (true | false) default false
26320 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26323 * Create a new Element
26324 * @param {Object} config The config object
26327 Roo.bootstrap.MessageBar = function(config){
26328 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26331 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26337 beforeClass: 'bootstrap-sticky-wrap',
26339 getAutoCreate : function(){
26343 cls: 'alert alert-dismissable alert-' + this.weight,
26348 html: this.html || ''
26354 cfg.cls += ' alert-messages-fixed';
26368 onRender : function(ct, position)
26370 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26373 var cfg = Roo.apply({}, this.getAutoCreate());
26377 cfg.cls += ' ' + this.cls;
26380 cfg.style = this.style;
26382 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26384 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26387 this.el.select('>button.close').on('click', this.hide, this);
26393 if (!this.rendered) {
26399 this.fireEvent('show', this);
26405 if (!this.rendered) {
26411 this.fireEvent('hide', this);
26414 update : function()
26416 // var e = this.el.dom.firstChild;
26418 // if(this.closable){
26419 // e = e.nextSibling;
26422 // e.data = this.html || '';
26424 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26440 * @class Roo.bootstrap.Graph
26441 * @extends Roo.bootstrap.Component
26442 * Bootstrap Graph class
26446 @cfg {String} graphtype bar | vbar | pie
26447 @cfg {number} g_x coodinator | centre x (pie)
26448 @cfg {number} g_y coodinator | centre y (pie)
26449 @cfg {number} g_r radius (pie)
26450 @cfg {number} g_height height of the chart (respected by all elements in the set)
26451 @cfg {number} g_width width of the chart (respected by all elements in the set)
26452 @cfg {Object} title The title of the chart
26455 -opts (object) options for the chart
26457 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26458 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26460 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.
26461 o stacked (boolean) whether or not to tread values as in a stacked bar chart
26463 o stretch (boolean)
26465 -opts (object) options for the pie
26468 o startAngle (number)
26469 o endAngle (number)
26473 * Create a new Input
26474 * @param {Object} config The config object
26477 Roo.bootstrap.Graph = function(config){
26478 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26484 * The img click event for the img.
26485 * @param {Roo.EventObject} e
26491 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
26502 //g_colors: this.colors,
26509 getAutoCreate : function(){
26520 onRender : function(ct,position){
26523 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26525 if (typeof(Raphael) == 'undefined') {
26526 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26530 this.raphael = Raphael(this.el.dom);
26532 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26533 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26534 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26535 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26537 r.text(160, 10, "Single Series Chart").attr(txtattr);
26538 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26539 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26540 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26542 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26543 r.barchart(330, 10, 300, 220, data1);
26544 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26545 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26548 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26549 // r.barchart(30, 30, 560, 250, xdata, {
26550 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26551 // axis : "0 0 1 1",
26552 // axisxlabels : xdata
26553 // //yvalues : cols,
26556 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26558 // this.load(null,xdata,{
26559 // axis : "0 0 1 1",
26560 // axisxlabels : xdata
26565 load : function(graphtype,xdata,opts)
26567 this.raphael.clear();
26569 graphtype = this.graphtype;
26574 var r = this.raphael,
26575 fin = function () {
26576 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26578 fout = function () {
26579 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26581 pfin = function() {
26582 this.sector.stop();
26583 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26586 this.label[0].stop();
26587 this.label[0].attr({ r: 7.5 });
26588 this.label[1].attr({ "font-weight": 800 });
26591 pfout = function() {
26592 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26595 this.label[0].animate({ r: 5 }, 500, "bounce");
26596 this.label[1].attr({ "font-weight": 400 });
26602 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26605 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26608 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
26609 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26611 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26618 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26623 setTitle: function(o)
26628 initEvents: function() {
26631 this.el.on('click', this.onClick, this);
26635 onClick : function(e)
26637 Roo.log('img onclick');
26638 this.fireEvent('click', this, e);
26650 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26653 * @class Roo.bootstrap.dash.NumberBox
26654 * @extends Roo.bootstrap.Component
26655 * Bootstrap NumberBox class
26656 * @cfg {String} headline Box headline
26657 * @cfg {String} content Box content
26658 * @cfg {String} icon Box icon
26659 * @cfg {String} footer Footer text
26660 * @cfg {String} fhref Footer href
26663 * Create a new NumberBox
26664 * @param {Object} config The config object
26668 Roo.bootstrap.dash.NumberBox = function(config){
26669 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26673 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26682 getAutoCreate : function(){
26686 cls : 'small-box ',
26694 cls : 'roo-headline',
26695 html : this.headline
26699 cls : 'roo-content',
26700 html : this.content
26714 cls : 'ion ' + this.icon
26723 cls : 'small-box-footer',
26724 href : this.fhref || '#',
26728 cfg.cn.push(footer);
26735 onRender : function(ct,position){
26736 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26743 setHeadline: function (value)
26745 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26748 setFooter: function (value, href)
26750 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26753 this.el.select('a.small-box-footer',true).first().attr('href', href);
26758 setContent: function (value)
26760 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26763 initEvents: function()
26777 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26780 * @class Roo.bootstrap.dash.TabBox
26781 * @extends Roo.bootstrap.Component
26782 * Bootstrap TabBox class
26783 * @cfg {String} title Title of the TabBox
26784 * @cfg {String} icon Icon of the TabBox
26785 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26786 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26789 * Create a new TabBox
26790 * @param {Object} config The config object
26794 Roo.bootstrap.dash.TabBox = function(config){
26795 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26800 * When a pane is added
26801 * @param {Roo.bootstrap.dash.TabPane} pane
26805 * @event activatepane
26806 * When a pane is activated
26807 * @param {Roo.bootstrap.dash.TabPane} pane
26809 "activatepane" : true
26817 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26822 tabScrollable : false,
26824 getChildContainer : function()
26826 return this.el.select('.tab-content', true).first();
26829 getAutoCreate : function(){
26833 cls: 'pull-left header',
26841 cls: 'fa ' + this.icon
26847 cls: 'nav nav-tabs pull-right',
26853 if(this.tabScrollable){
26860 cls: 'nav nav-tabs pull-right',
26871 cls: 'nav-tabs-custom',
26876 cls: 'tab-content no-padding',
26884 initEvents : function()
26886 //Roo.log('add add pane handler');
26887 this.on('addpane', this.onAddPane, this);
26890 * Updates the box title
26891 * @param {String} html to set the title to.
26893 setTitle : function(value)
26895 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26897 onAddPane : function(pane)
26899 this.panes.push(pane);
26900 //Roo.log('addpane');
26902 // tabs are rendere left to right..
26903 if(!this.showtabs){
26907 var ctr = this.el.select('.nav-tabs', true).first();
26910 var existing = ctr.select('.nav-tab',true);
26911 var qty = existing.getCount();;
26914 var tab = ctr.createChild({
26916 cls : 'nav-tab' + (qty ? '' : ' active'),
26924 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26927 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26929 pane.el.addClass('active');
26934 onTabClick : function(ev,un,ob,pane)
26936 //Roo.log('tab - prev default');
26937 ev.preventDefault();
26940 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26941 pane.tab.addClass('active');
26942 //Roo.log(pane.title);
26943 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26944 // technically we should have a deactivate event.. but maybe add later.
26945 // and it should not de-activate the selected tab...
26946 this.fireEvent('activatepane', pane);
26947 pane.el.addClass('active');
26948 pane.fireEvent('activate');
26953 getActivePane : function()
26956 Roo.each(this.panes, function(p) {
26957 if(p.el.hasClass('active')){
26978 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26980 * @class Roo.bootstrap.TabPane
26981 * @extends Roo.bootstrap.Component
26982 * Bootstrap TabPane class
26983 * @cfg {Boolean} active (false | true) Default false
26984 * @cfg {String} title title of panel
26988 * Create a new TabPane
26989 * @param {Object} config The config object
26992 Roo.bootstrap.dash.TabPane = function(config){
26993 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26999 * When a pane is activated
27000 * @param {Roo.bootstrap.dash.TabPane} pane
27007 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27012 // the tabBox that this is attached to.
27015 getAutoCreate : function()
27023 cfg.cls += ' active';
27028 initEvents : function()
27030 //Roo.log('trigger add pane handler');
27031 this.parent().fireEvent('addpane', this)
27035 * Updates the tab title
27036 * @param {String} html to set the title to.
27038 setTitle: function(str)
27044 this.tab.select('a', true).first().dom.innerHTML = str;
27061 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27064 * @class Roo.bootstrap.menu.Menu
27065 * @extends Roo.bootstrap.Component
27066 * Bootstrap Menu class - container for Menu
27067 * @cfg {String} html Text of the menu
27068 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27069 * @cfg {String} icon Font awesome icon
27070 * @cfg {String} pos Menu align to (top | bottom) default bottom
27074 * Create a new Menu
27075 * @param {Object} config The config object
27079 Roo.bootstrap.menu.Menu = function(config){
27080 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27084 * @event beforeshow
27085 * Fires before this menu is displayed
27086 * @param {Roo.bootstrap.menu.Menu} this
27090 * @event beforehide
27091 * Fires before this menu is hidden
27092 * @param {Roo.bootstrap.menu.Menu} this
27097 * Fires after this menu is displayed
27098 * @param {Roo.bootstrap.menu.Menu} this
27103 * Fires after this menu is hidden
27104 * @param {Roo.bootstrap.menu.Menu} this
27109 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27110 * @param {Roo.bootstrap.menu.Menu} this
27111 * @param {Roo.EventObject} e
27118 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27122 weight : 'default',
27127 getChildContainer : function() {
27128 if(this.isSubMenu){
27132 return this.el.select('ul.dropdown-menu', true).first();
27135 getAutoCreate : function()
27140 cls : 'roo-menu-text',
27148 cls : 'fa ' + this.icon
27159 cls : 'dropdown-button btn btn-' + this.weight,
27164 cls : 'dropdown-toggle btn btn-' + this.weight,
27174 cls : 'dropdown-menu'
27180 if(this.pos == 'top'){
27181 cfg.cls += ' dropup';
27184 if(this.isSubMenu){
27187 cls : 'dropdown-menu'
27194 onRender : function(ct, position)
27196 this.isSubMenu = ct.hasClass('dropdown-submenu');
27198 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27201 initEvents : function()
27203 if(this.isSubMenu){
27207 this.hidden = true;
27209 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27210 this.triggerEl.on('click', this.onTriggerPress, this);
27212 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27213 this.buttonEl.on('click', this.onClick, this);
27219 if(this.isSubMenu){
27223 return this.el.select('ul.dropdown-menu', true).first();
27226 onClick : function(e)
27228 this.fireEvent("click", this, e);
27231 onTriggerPress : function(e)
27233 if (this.isVisible()) {
27240 isVisible : function(){
27241 return !this.hidden;
27246 this.fireEvent("beforeshow", this);
27248 this.hidden = false;
27249 this.el.addClass('open');
27251 Roo.get(document).on("mouseup", this.onMouseUp, this);
27253 this.fireEvent("show", this);
27260 this.fireEvent("beforehide", this);
27262 this.hidden = true;
27263 this.el.removeClass('open');
27265 Roo.get(document).un("mouseup", this.onMouseUp);
27267 this.fireEvent("hide", this);
27270 onMouseUp : function()
27284 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27287 * @class Roo.bootstrap.menu.Item
27288 * @extends Roo.bootstrap.Component
27289 * Bootstrap MenuItem class
27290 * @cfg {Boolean} submenu (true | false) default false
27291 * @cfg {String} html text of the item
27292 * @cfg {String} href the link
27293 * @cfg {Boolean} disable (true | false) default false
27294 * @cfg {Boolean} preventDefault (true | false) default true
27295 * @cfg {String} icon Font awesome icon
27296 * @cfg {String} pos Submenu align to (left | right) default right
27300 * Create a new Item
27301 * @param {Object} config The config object
27305 Roo.bootstrap.menu.Item = function(config){
27306 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27310 * Fires when the mouse is hovering over this menu
27311 * @param {Roo.bootstrap.menu.Item} this
27312 * @param {Roo.EventObject} e
27317 * Fires when the mouse exits this menu
27318 * @param {Roo.bootstrap.menu.Item} this
27319 * @param {Roo.EventObject} e
27325 * The raw click event for the entire grid.
27326 * @param {Roo.EventObject} e
27332 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27337 preventDefault: true,
27342 getAutoCreate : function()
27347 cls : 'roo-menu-item-text',
27355 cls : 'fa ' + this.icon
27364 href : this.href || '#',
27371 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27375 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27377 if(this.pos == 'left'){
27378 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27385 initEvents : function()
27387 this.el.on('mouseover', this.onMouseOver, this);
27388 this.el.on('mouseout', this.onMouseOut, this);
27390 this.el.select('a', true).first().on('click', this.onClick, this);
27394 onClick : function(e)
27396 if(this.preventDefault){
27397 e.preventDefault();
27400 this.fireEvent("click", this, e);
27403 onMouseOver : function(e)
27405 if(this.submenu && this.pos == 'left'){
27406 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27409 this.fireEvent("mouseover", this, e);
27412 onMouseOut : function(e)
27414 this.fireEvent("mouseout", this, e);
27426 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27429 * @class Roo.bootstrap.menu.Separator
27430 * @extends Roo.bootstrap.Component
27431 * Bootstrap Separator class
27434 * Create a new Separator
27435 * @param {Object} config The config object
27439 Roo.bootstrap.menu.Separator = function(config){
27440 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27443 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
27445 getAutoCreate : function(){
27466 * @class Roo.bootstrap.Tooltip
27467 * Bootstrap Tooltip class
27468 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27469 * to determine which dom element triggers the tooltip.
27471 * It needs to add support for additional attributes like tooltip-position
27474 * Create a new Toolti
27475 * @param {Object} config The config object
27478 Roo.bootstrap.Tooltip = function(config){
27479 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27481 this.alignment = Roo.bootstrap.Tooltip.alignment;
27483 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27484 this.alignment = config.alignment;
27489 Roo.apply(Roo.bootstrap.Tooltip, {
27491 * @function init initialize tooltip monitoring.
27495 currentTip : false,
27496 currentRegion : false,
27502 Roo.get(document).on('mouseover', this.enter ,this);
27503 Roo.get(document).on('mouseout', this.leave, this);
27506 this.currentTip = new Roo.bootstrap.Tooltip();
27509 enter : function(ev)
27511 var dom = ev.getTarget();
27513 //Roo.log(['enter',dom]);
27514 var el = Roo.fly(dom);
27515 if (this.currentEl) {
27517 //Roo.log(this.currentEl);
27518 //Roo.log(this.currentEl.contains(dom));
27519 if (this.currentEl == el) {
27522 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27528 if (this.currentTip.el) {
27529 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27533 if(!el || el.dom == document){
27539 // you can not look for children, as if el is the body.. then everythign is the child..
27540 if (!el.attr('tooltip')) { //
27541 if (!el.select("[tooltip]").elements.length) {
27544 // is the mouse over this child...?
27545 bindEl = el.select("[tooltip]").first();
27546 var xy = ev.getXY();
27547 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27548 //Roo.log("not in region.");
27551 //Roo.log("child element over..");
27554 this.currentEl = bindEl;
27555 this.currentTip.bind(bindEl);
27556 this.currentRegion = Roo.lib.Region.getRegion(dom);
27557 this.currentTip.enter();
27560 leave : function(ev)
27562 var dom = ev.getTarget();
27563 //Roo.log(['leave',dom]);
27564 if (!this.currentEl) {
27569 if (dom != this.currentEl.dom) {
27572 var xy = ev.getXY();
27573 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
27576 // only activate leave if mouse cursor is outside... bounding box..
27581 if (this.currentTip) {
27582 this.currentTip.leave();
27584 //Roo.log('clear currentEl');
27585 this.currentEl = false;
27590 'left' : ['r-l', [-2,0], 'right'],
27591 'right' : ['l-r', [2,0], 'left'],
27592 'bottom' : ['t-b', [0,2], 'top'],
27593 'top' : [ 'b-t', [0,-2], 'bottom']
27599 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
27604 delay : null, // can be { show : 300 , hide: 500}
27608 hoverState : null, //???
27610 placement : 'bottom',
27614 getAutoCreate : function(){
27621 cls : 'tooltip-arrow'
27624 cls : 'tooltip-inner'
27631 bind : function(el)
27637 enter : function () {
27639 if (this.timeout != null) {
27640 clearTimeout(this.timeout);
27643 this.hoverState = 'in';
27644 //Roo.log("enter - show");
27645 if (!this.delay || !this.delay.show) {
27650 this.timeout = setTimeout(function () {
27651 if (_t.hoverState == 'in') {
27654 }, this.delay.show);
27658 clearTimeout(this.timeout);
27660 this.hoverState = 'out';
27661 if (!this.delay || !this.delay.hide) {
27667 this.timeout = setTimeout(function () {
27668 //Roo.log("leave - timeout");
27670 if (_t.hoverState == 'out') {
27672 Roo.bootstrap.Tooltip.currentEl = false;
27677 show : function (msg)
27680 this.render(document.body);
27683 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27685 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27687 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27689 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27691 var placement = typeof this.placement == 'function' ?
27692 this.placement.call(this, this.el, on_el) :
27695 var autoToken = /\s?auto?\s?/i;
27696 var autoPlace = autoToken.test(placement);
27698 placement = placement.replace(autoToken, '') || 'top';
27702 //this.el.setXY([0,0]);
27704 //this.el.dom.style.display='block';
27706 //this.el.appendTo(on_el);
27708 var p = this.getPosition();
27709 var box = this.el.getBox();
27715 var align = this.alignment[placement];
27717 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27719 if(placement == 'top' || placement == 'bottom'){
27721 placement = 'right';
27724 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27725 placement = 'left';
27728 var scroll = Roo.select('body', true).first().getScroll();
27730 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27734 align = this.alignment[placement];
27737 this.el.alignTo(this.bindEl, align[0],align[1]);
27738 //var arrow = this.el.select('.arrow',true).first();
27739 //arrow.set(align[2],
27741 this.el.addClass(placement);
27743 this.el.addClass('in fade');
27745 this.hoverState = null;
27747 if (this.el.hasClass('fade')) {
27758 //this.el.setXY([0,0]);
27759 this.el.removeClass('in');
27775 * @class Roo.bootstrap.LocationPicker
27776 * @extends Roo.bootstrap.Component
27777 * Bootstrap LocationPicker class
27778 * @cfg {Number} latitude Position when init default 0
27779 * @cfg {Number} longitude Position when init default 0
27780 * @cfg {Number} zoom default 15
27781 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27782 * @cfg {Boolean} mapTypeControl default false
27783 * @cfg {Boolean} disableDoubleClickZoom default false
27784 * @cfg {Boolean} scrollwheel default true
27785 * @cfg {Boolean} streetViewControl default false
27786 * @cfg {Number} radius default 0
27787 * @cfg {String} locationName
27788 * @cfg {Boolean} draggable default true
27789 * @cfg {Boolean} enableAutocomplete default false
27790 * @cfg {Boolean} enableReverseGeocode default true
27791 * @cfg {String} markerTitle
27794 * Create a new LocationPicker
27795 * @param {Object} config The config object
27799 Roo.bootstrap.LocationPicker = function(config){
27801 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27806 * Fires when the picker initialized.
27807 * @param {Roo.bootstrap.LocationPicker} this
27808 * @param {Google Location} location
27812 * @event positionchanged
27813 * Fires when the picker position changed.
27814 * @param {Roo.bootstrap.LocationPicker} this
27815 * @param {Google Location} location
27817 positionchanged : true,
27820 * Fires when the map resize.
27821 * @param {Roo.bootstrap.LocationPicker} this
27826 * Fires when the map show.
27827 * @param {Roo.bootstrap.LocationPicker} this
27832 * Fires when the map hide.
27833 * @param {Roo.bootstrap.LocationPicker} this
27838 * Fires when click the map.
27839 * @param {Roo.bootstrap.LocationPicker} this
27840 * @param {Map event} e
27844 * @event mapRightClick
27845 * Fires when right click the map.
27846 * @param {Roo.bootstrap.LocationPicker} this
27847 * @param {Map event} e
27849 mapRightClick : true,
27851 * @event markerClick
27852 * Fires when click the marker.
27853 * @param {Roo.bootstrap.LocationPicker} this
27854 * @param {Map event} e
27856 markerClick : true,
27858 * @event markerRightClick
27859 * Fires when right click the marker.
27860 * @param {Roo.bootstrap.LocationPicker} this
27861 * @param {Map event} e
27863 markerRightClick : true,
27865 * @event OverlayViewDraw
27866 * Fires when OverlayView Draw
27867 * @param {Roo.bootstrap.LocationPicker} this
27869 OverlayViewDraw : true,
27871 * @event OverlayViewOnAdd
27872 * Fires when OverlayView Draw
27873 * @param {Roo.bootstrap.LocationPicker} this
27875 OverlayViewOnAdd : true,
27877 * @event OverlayViewOnRemove
27878 * Fires when OverlayView Draw
27879 * @param {Roo.bootstrap.LocationPicker} this
27881 OverlayViewOnRemove : true,
27883 * @event OverlayViewShow
27884 * Fires when OverlayView Draw
27885 * @param {Roo.bootstrap.LocationPicker} this
27886 * @param {Pixel} cpx
27888 OverlayViewShow : true,
27890 * @event OverlayViewHide
27891 * Fires when OverlayView Draw
27892 * @param {Roo.bootstrap.LocationPicker} this
27894 OverlayViewHide : true,
27896 * @event loadexception
27897 * Fires when load google lib failed.
27898 * @param {Roo.bootstrap.LocationPicker} this
27900 loadexception : true
27905 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27907 gMapContext: false,
27913 mapTypeControl: false,
27914 disableDoubleClickZoom: false,
27916 streetViewControl: false,
27920 enableAutocomplete: false,
27921 enableReverseGeocode: true,
27924 getAutoCreate: function()
27929 cls: 'roo-location-picker'
27935 initEvents: function(ct, position)
27937 if(!this.el.getWidth() || this.isApplied()){
27941 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27946 initial: function()
27948 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27949 this.fireEvent('loadexception', this);
27953 if(!this.mapTypeId){
27954 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27957 this.gMapContext = this.GMapContext();
27959 this.initOverlayView();
27961 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27965 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27966 _this.setPosition(_this.gMapContext.marker.position);
27969 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27970 _this.fireEvent('mapClick', this, event);
27974 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27975 _this.fireEvent('mapRightClick', this, event);
27979 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27980 _this.fireEvent('markerClick', this, event);
27984 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27985 _this.fireEvent('markerRightClick', this, event);
27989 this.setPosition(this.gMapContext.location);
27991 this.fireEvent('initial', this, this.gMapContext.location);
27994 initOverlayView: function()
27998 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28002 _this.fireEvent('OverlayViewDraw', _this);
28007 _this.fireEvent('OverlayViewOnAdd', _this);
28010 onRemove: function()
28012 _this.fireEvent('OverlayViewOnRemove', _this);
28015 show: function(cpx)
28017 _this.fireEvent('OverlayViewShow', _this, cpx);
28022 _this.fireEvent('OverlayViewHide', _this);
28028 fromLatLngToContainerPixel: function(event)
28030 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28033 isApplied: function()
28035 return this.getGmapContext() == false ? false : true;
28038 getGmapContext: function()
28040 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28043 GMapContext: function()
28045 var position = new google.maps.LatLng(this.latitude, this.longitude);
28047 var _map = new google.maps.Map(this.el.dom, {
28050 mapTypeId: this.mapTypeId,
28051 mapTypeControl: this.mapTypeControl,
28052 disableDoubleClickZoom: this.disableDoubleClickZoom,
28053 scrollwheel: this.scrollwheel,
28054 streetViewControl: this.streetViewControl,
28055 locationName: this.locationName,
28056 draggable: this.draggable,
28057 enableAutocomplete: this.enableAutocomplete,
28058 enableReverseGeocode: this.enableReverseGeocode
28061 var _marker = new google.maps.Marker({
28062 position: position,
28064 title: this.markerTitle,
28065 draggable: this.draggable
28072 location: position,
28073 radius: this.radius,
28074 locationName: this.locationName,
28075 addressComponents: {
28076 formatted_address: null,
28077 addressLine1: null,
28078 addressLine2: null,
28080 streetNumber: null,
28084 stateOrProvince: null
28087 domContainer: this.el.dom,
28088 geodecoder: new google.maps.Geocoder()
28092 drawCircle: function(center, radius, options)
28094 if (this.gMapContext.circle != null) {
28095 this.gMapContext.circle.setMap(null);
28099 options = Roo.apply({}, options, {
28100 strokeColor: "#0000FF",
28101 strokeOpacity: .35,
28103 fillColor: "#0000FF",
28107 options.map = this.gMapContext.map;
28108 options.radius = radius;
28109 options.center = center;
28110 this.gMapContext.circle = new google.maps.Circle(options);
28111 return this.gMapContext.circle;
28117 setPosition: function(location)
28119 this.gMapContext.location = location;
28120 this.gMapContext.marker.setPosition(location);
28121 this.gMapContext.map.panTo(location);
28122 this.drawCircle(location, this.gMapContext.radius, {});
28126 if (this.gMapContext.settings.enableReverseGeocode) {
28127 this.gMapContext.geodecoder.geocode({
28128 latLng: this.gMapContext.location
28129 }, function(results, status) {
28131 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28132 _this.gMapContext.locationName = results[0].formatted_address;
28133 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28135 _this.fireEvent('positionchanged', this, location);
28142 this.fireEvent('positionchanged', this, location);
28147 google.maps.event.trigger(this.gMapContext.map, "resize");
28149 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28151 this.fireEvent('resize', this);
28154 setPositionByLatLng: function(latitude, longitude)
28156 this.setPosition(new google.maps.LatLng(latitude, longitude));
28159 getCurrentPosition: function()
28162 latitude: this.gMapContext.location.lat(),
28163 longitude: this.gMapContext.location.lng()
28167 getAddressName: function()
28169 return this.gMapContext.locationName;
28172 getAddressComponents: function()
28174 return this.gMapContext.addressComponents;
28177 address_component_from_google_geocode: function(address_components)
28181 for (var i = 0; i < address_components.length; i++) {
28182 var component = address_components[i];
28183 if (component.types.indexOf("postal_code") >= 0) {
28184 result.postalCode = component.short_name;
28185 } else if (component.types.indexOf("street_number") >= 0) {
28186 result.streetNumber = component.short_name;
28187 } else if (component.types.indexOf("route") >= 0) {
28188 result.streetName = component.short_name;
28189 } else if (component.types.indexOf("neighborhood") >= 0) {
28190 result.city = component.short_name;
28191 } else if (component.types.indexOf("locality") >= 0) {
28192 result.city = component.short_name;
28193 } else if (component.types.indexOf("sublocality") >= 0) {
28194 result.district = component.short_name;
28195 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28196 result.stateOrProvince = component.short_name;
28197 } else if (component.types.indexOf("country") >= 0) {
28198 result.country = component.short_name;
28202 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28203 result.addressLine2 = "";
28207 setZoomLevel: function(zoom)
28209 this.gMapContext.map.setZoom(zoom);
28222 this.fireEvent('show', this);
28233 this.fireEvent('hide', this);
28238 Roo.apply(Roo.bootstrap.LocationPicker, {
28240 OverlayView : function(map, options)
28242 options = options || {};
28249 * @class Roo.bootstrap.Alert
28250 * @extends Roo.bootstrap.Component
28251 * Bootstrap Alert class - shows an alert area box
28253 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28254 Enter a valid email address
28257 * @cfg {String} title The title of alert
28258 * @cfg {String} html The content of alert
28259 * @cfg {String} weight ( success | info | warning | danger )
28260 * @cfg {String} faicon font-awesomeicon
28263 * Create a new alert
28264 * @param {Object} config The config object
28268 Roo.bootstrap.Alert = function(config){
28269 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28273 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28280 getAutoCreate : function()
28289 cls : 'roo-alert-icon'
28294 cls : 'roo-alert-title',
28299 cls : 'roo-alert-text',
28306 cfg.cn[0].cls += ' fa ' + this.faicon;
28310 cfg.cls += ' alert-' + this.weight;
28316 initEvents: function()
28318 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28321 setTitle : function(str)
28323 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28326 setText : function(str)
28328 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28331 setWeight : function(weight)
28334 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28337 this.weight = weight;
28339 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28342 setIcon : function(icon)
28345 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28348 this.faicon = icon;
28350 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28371 * @class Roo.bootstrap.UploadCropbox
28372 * @extends Roo.bootstrap.Component
28373 * Bootstrap UploadCropbox class
28374 * @cfg {String} emptyText show when image has been loaded
28375 * @cfg {String} rotateNotify show when image too small to rotate
28376 * @cfg {Number} errorTimeout default 3000
28377 * @cfg {Number} minWidth default 300
28378 * @cfg {Number} minHeight default 300
28379 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28380 * @cfg {Boolean} isDocument (true|false) default false
28381 * @cfg {String} url action url
28382 * @cfg {String} paramName default 'imageUpload'
28383 * @cfg {String} method default POST
28384 * @cfg {Boolean} loadMask (true|false) default true
28385 * @cfg {Boolean} loadingText default 'Loading...'
28388 * Create a new UploadCropbox
28389 * @param {Object} config The config object
28392 Roo.bootstrap.UploadCropbox = function(config){
28393 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28397 * @event beforeselectfile
28398 * Fire before select file
28399 * @param {Roo.bootstrap.UploadCropbox} this
28401 "beforeselectfile" : true,
28404 * Fire after initEvent
28405 * @param {Roo.bootstrap.UploadCropbox} this
28410 * Fire after initEvent
28411 * @param {Roo.bootstrap.UploadCropbox} this
28412 * @param {String} data
28417 * Fire when preparing the file data
28418 * @param {Roo.bootstrap.UploadCropbox} this
28419 * @param {Object} file
28424 * Fire when get exception
28425 * @param {Roo.bootstrap.UploadCropbox} this
28426 * @param {XMLHttpRequest} xhr
28428 "exception" : true,
28430 * @event beforeloadcanvas
28431 * Fire before load the canvas
28432 * @param {Roo.bootstrap.UploadCropbox} this
28433 * @param {String} src
28435 "beforeloadcanvas" : true,
28438 * Fire when trash image
28439 * @param {Roo.bootstrap.UploadCropbox} this
28444 * Fire when download the image
28445 * @param {Roo.bootstrap.UploadCropbox} this
28449 * @event footerbuttonclick
28450 * Fire when footerbuttonclick
28451 * @param {Roo.bootstrap.UploadCropbox} this
28452 * @param {String} type
28454 "footerbuttonclick" : true,
28458 * @param {Roo.bootstrap.UploadCropbox} this
28463 * Fire when rotate the image
28464 * @param {Roo.bootstrap.UploadCropbox} this
28465 * @param {String} pos
28470 * Fire when inspect the file
28471 * @param {Roo.bootstrap.UploadCropbox} this
28472 * @param {Object} file
28477 * Fire when xhr upload the file
28478 * @param {Roo.bootstrap.UploadCropbox} this
28479 * @param {Object} data
28484 * Fire when arrange the file data
28485 * @param {Roo.bootstrap.UploadCropbox} this
28486 * @param {Object} formData
28491 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28494 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
28496 emptyText : 'Click to upload image',
28497 rotateNotify : 'Image is too small to rotate',
28498 errorTimeout : 3000,
28512 cropType : 'image/jpeg',
28514 canvasLoaded : false,
28515 isDocument : false,
28517 paramName : 'imageUpload',
28519 loadingText : 'Loading...',
28522 getAutoCreate : function()
28526 cls : 'roo-upload-cropbox',
28530 cls : 'roo-upload-cropbox-selector',
28535 cls : 'roo-upload-cropbox-body',
28536 style : 'cursor:pointer',
28540 cls : 'roo-upload-cropbox-preview'
28544 cls : 'roo-upload-cropbox-thumb'
28548 cls : 'roo-upload-cropbox-empty-notify',
28549 html : this.emptyText
28553 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28554 html : this.rotateNotify
28560 cls : 'roo-upload-cropbox-footer',
28563 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28573 onRender : function(ct, position)
28575 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28577 if (this.buttons.length) {
28579 Roo.each(this.buttons, function(bb) {
28581 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28583 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28589 this.maskEl = this.el;
28593 initEvents : function()
28595 this.urlAPI = (window.createObjectURL && window) ||
28596 (window.URL && URL.revokeObjectURL && URL) ||
28597 (window.webkitURL && webkitURL);
28599 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28600 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28602 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28603 this.selectorEl.hide();
28605 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28606 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28608 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28609 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28610 this.thumbEl.hide();
28612 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28613 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28615 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28616 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28617 this.errorEl.hide();
28619 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28620 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28621 this.footerEl.hide();
28623 this.setThumbBoxSize();
28629 this.fireEvent('initial', this);
28636 window.addEventListener("resize", function() { _this.resize(); } );
28638 this.bodyEl.on('click', this.beforeSelectFile, this);
28641 this.bodyEl.on('touchstart', this.onTouchStart, this);
28642 this.bodyEl.on('touchmove', this.onTouchMove, this);
28643 this.bodyEl.on('touchend', this.onTouchEnd, this);
28647 this.bodyEl.on('mousedown', this.onMouseDown, this);
28648 this.bodyEl.on('mousemove', this.onMouseMove, this);
28649 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28650 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28651 Roo.get(document).on('mouseup', this.onMouseUp, this);
28654 this.selectorEl.on('change', this.onFileSelected, this);
28660 this.baseScale = 1;
28662 this.baseRotate = 1;
28663 this.dragable = false;
28664 this.pinching = false;
28667 this.cropData = false;
28668 this.notifyEl.dom.innerHTML = this.emptyText;
28670 this.selectorEl.dom.value = '';
28674 resize : function()
28676 if(this.fireEvent('resize', this) != false){
28677 this.setThumbBoxPosition();
28678 this.setCanvasPosition();
28682 onFooterButtonClick : function(e, el, o, type)
28685 case 'rotate-left' :
28686 this.onRotateLeft(e);
28688 case 'rotate-right' :
28689 this.onRotateRight(e);
28692 this.beforeSelectFile(e);
28707 this.fireEvent('footerbuttonclick', this, type);
28710 beforeSelectFile : function(e)
28712 e.preventDefault();
28714 if(this.fireEvent('beforeselectfile', this) != false){
28715 this.selectorEl.dom.click();
28719 onFileSelected : function(e)
28721 e.preventDefault();
28723 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28727 var file = this.selectorEl.dom.files[0];
28729 if(this.fireEvent('inspect', this, file) != false){
28730 this.prepare(file);
28735 trash : function(e)
28737 this.fireEvent('trash', this);
28740 download : function(e)
28742 this.fireEvent('download', this);
28745 loadCanvas : function(src)
28747 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28751 this.imageEl = document.createElement('img');
28755 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28757 this.imageEl.src = src;
28761 onLoadCanvas : function()
28763 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28764 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28766 this.bodyEl.un('click', this.beforeSelectFile, this);
28768 this.notifyEl.hide();
28769 this.thumbEl.show();
28770 this.footerEl.show();
28772 this.baseRotateLevel();
28774 if(this.isDocument){
28775 this.setThumbBoxSize();
28778 this.setThumbBoxPosition();
28780 this.baseScaleLevel();
28786 this.canvasLoaded = true;
28789 this.maskEl.unmask();
28794 setCanvasPosition : function()
28796 if(!this.canvasEl){
28800 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28801 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28803 this.previewEl.setLeft(pw);
28804 this.previewEl.setTop(ph);
28808 onMouseDown : function(e)
28812 this.dragable = true;
28813 this.pinching = false;
28815 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28816 this.dragable = false;
28820 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28821 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28825 onMouseMove : function(e)
28829 if(!this.canvasLoaded){
28833 if (!this.dragable){
28837 var minX = Math.ceil(this.thumbEl.getLeft(true));
28838 var minY = Math.ceil(this.thumbEl.getTop(true));
28840 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28841 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28843 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28844 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28846 x = x - this.mouseX;
28847 y = y - this.mouseY;
28849 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28850 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28852 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28853 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28855 this.previewEl.setLeft(bgX);
28856 this.previewEl.setTop(bgY);
28858 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28859 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28862 onMouseUp : function(e)
28866 this.dragable = false;
28869 onMouseWheel : function(e)
28873 this.startScale = this.scale;
28875 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28877 if(!this.zoomable()){
28878 this.scale = this.startScale;
28887 zoomable : function()
28889 var minScale = this.thumbEl.getWidth() / this.minWidth;
28891 if(this.minWidth < this.minHeight){
28892 minScale = this.thumbEl.getHeight() / this.minHeight;
28895 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28896 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28900 (this.rotate == 0 || this.rotate == 180) &&
28902 width > this.imageEl.OriginWidth ||
28903 height > this.imageEl.OriginHeight ||
28904 (width < this.minWidth && height < this.minHeight)
28912 (this.rotate == 90 || this.rotate == 270) &&
28914 width > this.imageEl.OriginWidth ||
28915 height > this.imageEl.OriginHeight ||
28916 (width < this.minHeight && height < this.minWidth)
28923 !this.isDocument &&
28924 (this.rotate == 0 || this.rotate == 180) &&
28926 width < this.minWidth ||
28927 width > this.imageEl.OriginWidth ||
28928 height < this.minHeight ||
28929 height > this.imageEl.OriginHeight
28936 !this.isDocument &&
28937 (this.rotate == 90 || this.rotate == 270) &&
28939 width < this.minHeight ||
28940 width > this.imageEl.OriginWidth ||
28941 height < this.minWidth ||
28942 height > this.imageEl.OriginHeight
28952 onRotateLeft : function(e)
28954 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28956 var minScale = this.thumbEl.getWidth() / this.minWidth;
28958 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28959 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28961 this.startScale = this.scale;
28963 while (this.getScaleLevel() < minScale){
28965 this.scale = this.scale + 1;
28967 if(!this.zoomable()){
28972 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28973 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28978 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28985 this.scale = this.startScale;
28987 this.onRotateFail();
28992 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28994 if(this.isDocument){
28995 this.setThumbBoxSize();
28996 this.setThumbBoxPosition();
28997 this.setCanvasPosition();
29002 this.fireEvent('rotate', this, 'left');
29006 onRotateRight : function(e)
29008 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29010 var minScale = this.thumbEl.getWidth() / this.minWidth;
29012 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29013 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29015 this.startScale = this.scale;
29017 while (this.getScaleLevel() < minScale){
29019 this.scale = this.scale + 1;
29021 if(!this.zoomable()){
29026 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29027 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29032 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29039 this.scale = this.startScale;
29041 this.onRotateFail();
29046 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29048 if(this.isDocument){
29049 this.setThumbBoxSize();
29050 this.setThumbBoxPosition();
29051 this.setCanvasPosition();
29056 this.fireEvent('rotate', this, 'right');
29059 onRotateFail : function()
29061 this.errorEl.show(true);
29065 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29070 this.previewEl.dom.innerHTML = '';
29072 var canvasEl = document.createElement("canvas");
29074 var contextEl = canvasEl.getContext("2d");
29076 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29077 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29078 var center = this.imageEl.OriginWidth / 2;
29080 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29081 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29082 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29083 center = this.imageEl.OriginHeight / 2;
29086 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29088 contextEl.translate(center, center);
29089 contextEl.rotate(this.rotate * Math.PI / 180);
29091 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29093 this.canvasEl = document.createElement("canvas");
29095 this.contextEl = this.canvasEl.getContext("2d");
29097 switch (this.rotate) {
29100 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29101 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29103 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29108 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29109 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29111 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29112 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);
29116 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29121 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29122 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29124 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29125 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);
29129 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);
29134 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29135 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29137 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29138 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29142 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);
29149 this.previewEl.appendChild(this.canvasEl);
29151 this.setCanvasPosition();
29156 if(!this.canvasLoaded){
29160 var imageCanvas = document.createElement("canvas");
29162 var imageContext = imageCanvas.getContext("2d");
29164 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29165 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29167 var center = imageCanvas.width / 2;
29169 imageContext.translate(center, center);
29171 imageContext.rotate(this.rotate * Math.PI / 180);
29173 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29175 var canvas = document.createElement("canvas");
29177 var context = canvas.getContext("2d");
29179 canvas.width = this.minWidth;
29180 canvas.height = this.minHeight;
29182 switch (this.rotate) {
29185 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29186 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29188 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29189 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29191 var targetWidth = this.minWidth - 2 * x;
29192 var targetHeight = this.minHeight - 2 * y;
29196 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29197 scale = targetWidth / width;
29200 if(x > 0 && y == 0){
29201 scale = targetHeight / height;
29204 if(x > 0 && y > 0){
29205 scale = targetWidth / width;
29207 if(width < height){
29208 scale = targetHeight / height;
29212 context.scale(scale, scale);
29214 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29215 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29217 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29218 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29220 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29225 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29226 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29228 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29229 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29231 var targetWidth = this.minWidth - 2 * x;
29232 var targetHeight = this.minHeight - 2 * y;
29236 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29237 scale = targetWidth / width;
29240 if(x > 0 && y == 0){
29241 scale = targetHeight / height;
29244 if(x > 0 && y > 0){
29245 scale = targetWidth / width;
29247 if(width < height){
29248 scale = targetHeight / height;
29252 context.scale(scale, scale);
29254 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29255 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29257 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29258 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29260 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29262 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29267 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29268 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29270 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29271 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29273 var targetWidth = this.minWidth - 2 * x;
29274 var targetHeight = this.minHeight - 2 * y;
29278 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29279 scale = targetWidth / width;
29282 if(x > 0 && y == 0){
29283 scale = targetHeight / height;
29286 if(x > 0 && y > 0){
29287 scale = targetWidth / width;
29289 if(width < height){
29290 scale = targetHeight / height;
29294 context.scale(scale, scale);
29296 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29297 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29299 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29300 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29302 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29303 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29305 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29310 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29311 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29313 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29314 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29316 var targetWidth = this.minWidth - 2 * x;
29317 var targetHeight = this.minHeight - 2 * y;
29321 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29322 scale = targetWidth / width;
29325 if(x > 0 && y == 0){
29326 scale = targetHeight / height;
29329 if(x > 0 && y > 0){
29330 scale = targetWidth / width;
29332 if(width < height){
29333 scale = targetHeight / height;
29337 context.scale(scale, scale);
29339 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29340 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29342 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29343 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29345 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29347 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29354 this.cropData = canvas.toDataURL(this.cropType);
29356 if(this.fireEvent('crop', this, this.cropData) !== false){
29357 this.process(this.file, this.cropData);
29364 setThumbBoxSize : function()
29368 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29369 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29370 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29372 this.minWidth = width;
29373 this.minHeight = height;
29375 if(this.rotate == 90 || this.rotate == 270){
29376 this.minWidth = height;
29377 this.minHeight = width;
29382 width = Math.ceil(this.minWidth * height / this.minHeight);
29384 if(this.minWidth > this.minHeight){
29386 height = Math.ceil(this.minHeight * width / this.minWidth);
29389 this.thumbEl.setStyle({
29390 width : width + 'px',
29391 height : height + 'px'
29398 setThumbBoxPosition : function()
29400 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29401 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29403 this.thumbEl.setLeft(x);
29404 this.thumbEl.setTop(y);
29408 baseRotateLevel : function()
29410 this.baseRotate = 1;
29413 typeof(this.exif) != 'undefined' &&
29414 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29415 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29417 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29420 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29424 baseScaleLevel : function()
29428 if(this.isDocument){
29430 if(this.baseRotate == 6 || this.baseRotate == 8){
29432 height = this.thumbEl.getHeight();
29433 this.baseScale = height / this.imageEl.OriginWidth;
29435 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29436 width = this.thumbEl.getWidth();
29437 this.baseScale = width / 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;
29454 if(this.baseRotate == 6 || this.baseRotate == 8){
29456 width = this.thumbEl.getHeight();
29457 this.baseScale = width / this.imageEl.OriginHeight;
29459 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29460 height = this.thumbEl.getWidth();
29461 this.baseScale = height / this.imageEl.OriginHeight;
29464 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29465 height = this.thumbEl.getWidth();
29466 this.baseScale = height / this.imageEl.OriginHeight;
29468 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29469 width = this.thumbEl.getHeight();
29470 this.baseScale = width / this.imageEl.OriginWidth;
29477 width = this.thumbEl.getWidth();
29478 this.baseScale = width / this.imageEl.OriginWidth;
29480 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29481 height = this.thumbEl.getHeight();
29482 this.baseScale = height / this.imageEl.OriginHeight;
29485 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29487 height = this.thumbEl.getHeight();
29488 this.baseScale = height / this.imageEl.OriginHeight;
29490 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29491 width = this.thumbEl.getWidth();
29492 this.baseScale = width / this.imageEl.OriginWidth;
29500 getScaleLevel : function()
29502 return this.baseScale * Math.pow(1.1, this.scale);
29505 onTouchStart : function(e)
29507 if(!this.canvasLoaded){
29508 this.beforeSelectFile(e);
29512 var touches = e.browserEvent.touches;
29518 if(touches.length == 1){
29519 this.onMouseDown(e);
29523 if(touches.length != 2){
29529 for(var i = 0, finger; finger = touches[i]; i++){
29530 coords.push(finger.pageX, finger.pageY);
29533 var x = Math.pow(coords[0] - coords[2], 2);
29534 var y = Math.pow(coords[1] - coords[3], 2);
29536 this.startDistance = Math.sqrt(x + y);
29538 this.startScale = this.scale;
29540 this.pinching = true;
29541 this.dragable = false;
29545 onTouchMove : function(e)
29547 if(!this.pinching && !this.dragable){
29551 var touches = e.browserEvent.touches;
29558 this.onMouseMove(e);
29564 for(var i = 0, finger; finger = touches[i]; i++){
29565 coords.push(finger.pageX, finger.pageY);
29568 var x = Math.pow(coords[0] - coords[2], 2);
29569 var y = Math.pow(coords[1] - coords[3], 2);
29571 this.endDistance = Math.sqrt(x + y);
29573 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29575 if(!this.zoomable()){
29576 this.scale = this.startScale;
29584 onTouchEnd : function(e)
29586 this.pinching = false;
29587 this.dragable = false;
29591 process : function(file, crop)
29594 this.maskEl.mask(this.loadingText);
29597 this.xhr = new XMLHttpRequest();
29599 file.xhr = this.xhr;
29601 this.xhr.open(this.method, this.url, true);
29604 "Accept": "application/json",
29605 "Cache-Control": "no-cache",
29606 "X-Requested-With": "XMLHttpRequest"
29609 for (var headerName in headers) {
29610 var headerValue = headers[headerName];
29612 this.xhr.setRequestHeader(headerName, headerValue);
29618 this.xhr.onload = function()
29620 _this.xhrOnLoad(_this.xhr);
29623 this.xhr.onerror = function()
29625 _this.xhrOnError(_this.xhr);
29628 var formData = new FormData();
29630 formData.append('returnHTML', 'NO');
29633 formData.append('crop', crop);
29636 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29637 formData.append(this.paramName, file, file.name);
29640 if(typeof(file.filename) != 'undefined'){
29641 formData.append('filename', file.filename);
29644 if(typeof(file.mimetype) != 'undefined'){
29645 formData.append('mimetype', file.mimetype);
29648 if(this.fireEvent('arrange', this, formData) != false){
29649 this.xhr.send(formData);
29653 xhrOnLoad : function(xhr)
29656 this.maskEl.unmask();
29659 if (xhr.readyState !== 4) {
29660 this.fireEvent('exception', this, xhr);
29664 var response = Roo.decode(xhr.responseText);
29666 if(!response.success){
29667 this.fireEvent('exception', this, xhr);
29671 var response = Roo.decode(xhr.responseText);
29673 this.fireEvent('upload', this, response);
29677 xhrOnError : function()
29680 this.maskEl.unmask();
29683 Roo.log('xhr on error');
29685 var response = Roo.decode(xhr.responseText);
29691 prepare : function(file)
29694 this.maskEl.mask(this.loadingText);
29700 if(typeof(file) === 'string'){
29701 this.loadCanvas(file);
29705 if(!file || !this.urlAPI){
29710 this.cropType = file.type;
29714 if(this.fireEvent('prepare', this, this.file) != false){
29716 var reader = new FileReader();
29718 reader.onload = function (e) {
29719 if (e.target.error) {
29720 Roo.log(e.target.error);
29724 var buffer = e.target.result,
29725 dataView = new DataView(buffer),
29727 maxOffset = dataView.byteLength - 4,
29731 if (dataView.getUint16(0) === 0xffd8) {
29732 while (offset < maxOffset) {
29733 markerBytes = dataView.getUint16(offset);
29735 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29736 markerLength = dataView.getUint16(offset + 2) + 2;
29737 if (offset + markerLength > dataView.byteLength) {
29738 Roo.log('Invalid meta data: Invalid segment size.');
29742 if(markerBytes == 0xffe1){
29743 _this.parseExifData(
29750 offset += markerLength;
29760 var url = _this.urlAPI.createObjectURL(_this.file);
29762 _this.loadCanvas(url);
29767 reader.readAsArrayBuffer(this.file);
29773 parseExifData : function(dataView, offset, length)
29775 var tiffOffset = offset + 10,
29779 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29780 // No Exif data, might be XMP data instead
29784 // Check for the ASCII code for "Exif" (0x45786966):
29785 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29786 // No Exif data, might be XMP data instead
29789 if (tiffOffset + 8 > dataView.byteLength) {
29790 Roo.log('Invalid Exif data: Invalid segment size.');
29793 // Check for the two null bytes:
29794 if (dataView.getUint16(offset + 8) !== 0x0000) {
29795 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29798 // Check the byte alignment:
29799 switch (dataView.getUint16(tiffOffset)) {
29801 littleEndian = true;
29804 littleEndian = false;
29807 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29810 // Check for the TIFF tag marker (0x002A):
29811 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29812 Roo.log('Invalid Exif data: Missing TIFF marker.');
29815 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29816 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29818 this.parseExifTags(
29821 tiffOffset + dirOffset,
29826 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29831 if (dirOffset + 6 > dataView.byteLength) {
29832 Roo.log('Invalid Exif data: Invalid directory offset.');
29835 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29836 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29837 if (dirEndOffset + 4 > dataView.byteLength) {
29838 Roo.log('Invalid Exif data: Invalid directory size.');
29841 for (i = 0; i < tagsNumber; i += 1) {
29845 dirOffset + 2 + 12 * i, // tag offset
29849 // Return the offset to the next directory:
29850 return dataView.getUint32(dirEndOffset, littleEndian);
29853 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29855 var tag = dataView.getUint16(offset, littleEndian);
29857 this.exif[tag] = this.getExifValue(
29861 dataView.getUint16(offset + 2, littleEndian), // tag type
29862 dataView.getUint32(offset + 4, littleEndian), // tag length
29867 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29869 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29878 Roo.log('Invalid Exif data: Invalid tag type.');
29882 tagSize = tagType.size * length;
29883 // Determine if the value is contained in the dataOffset bytes,
29884 // or if the value at the dataOffset is a pointer to the actual data:
29885 dataOffset = tagSize > 4 ?
29886 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29887 if (dataOffset + tagSize > dataView.byteLength) {
29888 Roo.log('Invalid Exif data: Invalid data offset.');
29891 if (length === 1) {
29892 return tagType.getValue(dataView, dataOffset, littleEndian);
29895 for (i = 0; i < length; i += 1) {
29896 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29899 if (tagType.ascii) {
29901 // Concatenate the chars:
29902 for (i = 0; i < values.length; i += 1) {
29904 // Ignore the terminating NULL byte(s):
29905 if (c === '\u0000') {
29917 Roo.apply(Roo.bootstrap.UploadCropbox, {
29919 'Orientation': 0x0112
29923 1: 0, //'top-left',
29925 3: 180, //'bottom-right',
29926 // 4: 'bottom-left',
29928 6: 90, //'right-top',
29929 // 7: 'right-bottom',
29930 8: 270 //'left-bottom'
29934 // byte, 8-bit unsigned int:
29936 getValue: function (dataView, dataOffset) {
29937 return dataView.getUint8(dataOffset);
29941 // ascii, 8-bit byte:
29943 getValue: function (dataView, dataOffset) {
29944 return String.fromCharCode(dataView.getUint8(dataOffset));
29949 // short, 16 bit int:
29951 getValue: function (dataView, dataOffset, littleEndian) {
29952 return dataView.getUint16(dataOffset, littleEndian);
29956 // long, 32 bit int:
29958 getValue: function (dataView, dataOffset, littleEndian) {
29959 return dataView.getUint32(dataOffset, littleEndian);
29963 // rational = two long values, first is numerator, second is denominator:
29965 getValue: function (dataView, dataOffset, littleEndian) {
29966 return dataView.getUint32(dataOffset, littleEndian) /
29967 dataView.getUint32(dataOffset + 4, littleEndian);
29971 // slong, 32 bit signed int:
29973 getValue: function (dataView, dataOffset, littleEndian) {
29974 return dataView.getInt32(dataOffset, littleEndian);
29978 // srational, two slongs, first is numerator, second is denominator:
29980 getValue: function (dataView, dataOffset, littleEndian) {
29981 return dataView.getInt32(dataOffset, littleEndian) /
29982 dataView.getInt32(dataOffset + 4, littleEndian);
29992 cls : 'btn-group roo-upload-cropbox-rotate-left',
29993 action : 'rotate-left',
29997 cls : 'btn btn-default',
29998 html : '<i class="fa fa-undo"></i>'
30004 cls : 'btn-group roo-upload-cropbox-picture',
30005 action : 'picture',
30009 cls : 'btn btn-default',
30010 html : '<i class="fa fa-picture-o"></i>'
30016 cls : 'btn-group roo-upload-cropbox-rotate-right',
30017 action : 'rotate-right',
30021 cls : 'btn btn-default',
30022 html : '<i class="fa fa-repeat"></i>'
30030 cls : 'btn-group roo-upload-cropbox-rotate-left',
30031 action : 'rotate-left',
30035 cls : 'btn btn-default',
30036 html : '<i class="fa fa-undo"></i>'
30042 cls : 'btn-group roo-upload-cropbox-download',
30043 action : 'download',
30047 cls : 'btn btn-default',
30048 html : '<i class="fa fa-download"></i>'
30054 cls : 'btn-group roo-upload-cropbox-crop',
30059 cls : 'btn btn-default',
30060 html : '<i class="fa fa-crop"></i>'
30066 cls : 'btn-group roo-upload-cropbox-trash',
30071 cls : 'btn btn-default',
30072 html : '<i class="fa fa-trash"></i>'
30078 cls : 'btn-group roo-upload-cropbox-rotate-right',
30079 action : 'rotate-right',
30083 cls : 'btn btn-default',
30084 html : '<i class="fa fa-repeat"></i>'
30092 cls : 'btn-group roo-upload-cropbox-rotate-left',
30093 action : 'rotate-left',
30097 cls : 'btn btn-default',
30098 html : '<i class="fa fa-undo"></i>'
30104 cls : 'btn-group roo-upload-cropbox-rotate-right',
30105 action : 'rotate-right',
30109 cls : 'btn btn-default',
30110 html : '<i class="fa fa-repeat"></i>'
30123 * @class Roo.bootstrap.DocumentManager
30124 * @extends Roo.bootstrap.Component
30125 * Bootstrap DocumentManager class
30126 * @cfg {String} paramName default 'imageUpload'
30127 * @cfg {String} toolTipName default 'filename'
30128 * @cfg {String} method default POST
30129 * @cfg {String} url action url
30130 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30131 * @cfg {Boolean} multiple multiple upload default true
30132 * @cfg {Number} thumbSize default 300
30133 * @cfg {String} fieldLabel
30134 * @cfg {Number} labelWidth default 4
30135 * @cfg {String} labelAlign (left|top) default left
30136 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30137 * @cfg {Number} labellg set the width of label (1-12)
30138 * @cfg {Number} labelmd set the width of label (1-12)
30139 * @cfg {Number} labelsm set the width of label (1-12)
30140 * @cfg {Number} labelxs set the width of label (1-12)
30143 * Create a new DocumentManager
30144 * @param {Object} config The config object
30147 Roo.bootstrap.DocumentManager = function(config){
30148 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30151 this.delegates = [];
30156 * Fire when initial the DocumentManager
30157 * @param {Roo.bootstrap.DocumentManager} this
30162 * inspect selected file
30163 * @param {Roo.bootstrap.DocumentManager} this
30164 * @param {File} file
30169 * Fire when xhr load exception
30170 * @param {Roo.bootstrap.DocumentManager} this
30171 * @param {XMLHttpRequest} xhr
30173 "exception" : true,
30175 * @event afterupload
30176 * Fire when xhr load exception
30177 * @param {Roo.bootstrap.DocumentManager} this
30178 * @param {XMLHttpRequest} xhr
30180 "afterupload" : true,
30183 * prepare the form data
30184 * @param {Roo.bootstrap.DocumentManager} this
30185 * @param {Object} formData
30190 * Fire when remove the file
30191 * @param {Roo.bootstrap.DocumentManager} this
30192 * @param {Object} file
30197 * Fire after refresh the file
30198 * @param {Roo.bootstrap.DocumentManager} this
30203 * Fire after click the image
30204 * @param {Roo.bootstrap.DocumentManager} this
30205 * @param {Object} file
30210 * Fire when upload a image and editable set to true
30211 * @param {Roo.bootstrap.DocumentManager} this
30212 * @param {Object} file
30216 * @event beforeselectfile
30217 * Fire before select file
30218 * @param {Roo.bootstrap.DocumentManager} this
30220 "beforeselectfile" : true,
30223 * Fire before process file
30224 * @param {Roo.bootstrap.DocumentManager} this
30225 * @param {Object} file
30229 * @event previewrendered
30230 * Fire when preview rendered
30231 * @param {Roo.bootstrap.DocumentManager} this
30232 * @param {Object} file
30234 "previewrendered" : true,
30237 "previewResize" : true
30242 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30251 paramName : 'imageUpload',
30252 toolTipName : 'filename',
30255 labelAlign : 'left',
30265 getAutoCreate : function()
30267 var managerWidget = {
30269 cls : 'roo-document-manager',
30273 cls : 'roo-document-manager-selector',
30278 cls : 'roo-document-manager-uploader',
30282 cls : 'roo-document-manager-upload-btn',
30283 html : '<i class="fa fa-plus"></i>'
30294 cls : 'column col-md-12',
30299 if(this.fieldLabel.length){
30304 cls : 'column col-md-12',
30305 html : this.fieldLabel
30309 cls : 'column col-md-12',
30314 if(this.labelAlign == 'left'){
30319 html : this.fieldLabel
30328 if(this.labelWidth > 12){
30329 content[0].style = "width: " + this.labelWidth + 'px';
30332 if(this.labelWidth < 13 && this.labelmd == 0){
30333 this.labelmd = this.labelWidth;
30336 if(this.labellg > 0){
30337 content[0].cls += ' col-lg-' + this.labellg;
30338 content[1].cls += ' col-lg-' + (12 - this.labellg);
30341 if(this.labelmd > 0){
30342 content[0].cls += ' col-md-' + this.labelmd;
30343 content[1].cls += ' col-md-' + (12 - this.labelmd);
30346 if(this.labelsm > 0){
30347 content[0].cls += ' col-sm-' + this.labelsm;
30348 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30351 if(this.labelxs > 0){
30352 content[0].cls += ' col-xs-' + this.labelxs;
30353 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30361 cls : 'row clearfix',
30369 initEvents : function()
30371 this.managerEl = this.el.select('.roo-document-manager', true).first();
30372 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30374 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30375 this.selectorEl.hide();
30378 this.selectorEl.attr('multiple', 'multiple');
30381 this.selectorEl.on('change', this.onFileSelected, this);
30383 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30384 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30386 this.uploader.on('click', this.onUploaderClick, this);
30388 this.renderProgressDialog();
30392 window.addEventListener("resize", function() { _this.refresh(); } );
30394 this.fireEvent('initial', this);
30397 renderProgressDialog : function()
30401 this.progressDialog = new Roo.bootstrap.Modal({
30402 cls : 'roo-document-manager-progress-dialog',
30403 allow_close : false,
30414 btnclick : function() {
30415 _this.uploadCancel();
30421 this.progressDialog.render(Roo.get(document.body));
30423 this.progress = new Roo.bootstrap.Progress({
30424 cls : 'roo-document-manager-progress',
30429 this.progress.render(this.progressDialog.getChildContainer());
30431 this.progressBar = new Roo.bootstrap.ProgressBar({
30432 cls : 'roo-document-manager-progress-bar',
30435 aria_valuemax : 12,
30439 this.progressBar.render(this.progress.getChildContainer());
30442 onUploaderClick : function(e)
30444 e.preventDefault();
30446 if(this.fireEvent('beforeselectfile', this) != false){
30447 this.selectorEl.dom.click();
30452 onFileSelected : function(e)
30454 e.preventDefault();
30456 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30460 Roo.each(this.selectorEl.dom.files, function(file){
30461 if(this.fireEvent('inspect', this, file) != false){
30462 this.files.push(file);
30472 this.selectorEl.dom.value = '';
30474 if(!this.files || !this.files.length){
30478 if(this.boxes > 0 && this.files.length > this.boxes){
30479 this.files = this.files.slice(0, this.boxes);
30482 this.uploader.show();
30484 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30485 this.uploader.hide();
30494 Roo.each(this.files, function(file){
30496 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30497 var f = this.renderPreview(file);
30502 if(file.type.indexOf('image') != -1){
30503 this.delegates.push(
30505 _this.process(file);
30506 }).createDelegate(this)
30514 _this.process(file);
30515 }).createDelegate(this)
30520 this.files = files;
30522 this.delegates = this.delegates.concat(docs);
30524 if(!this.delegates.length){
30529 this.progressBar.aria_valuemax = this.delegates.length;
30536 arrange : function()
30538 if(!this.delegates.length){
30539 this.progressDialog.hide();
30544 var delegate = this.delegates.shift();
30546 this.progressDialog.show();
30548 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30550 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30555 refresh : function()
30557 this.uploader.show();
30559 if(this.boxes > 0 && this.files.length > this.boxes - 1){
30560 this.uploader.hide();
30563 Roo.isTouch ? this.closable(false) : this.closable(true);
30565 this.fireEvent('refresh', this);
30568 onRemove : function(e, el, o)
30570 e.preventDefault();
30572 this.fireEvent('remove', this, o);
30576 remove : function(o)
30580 Roo.each(this.files, function(file){
30581 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30590 this.files = files;
30597 Roo.each(this.files, function(file){
30602 file.target.remove();
30611 onClick : function(e, el, o)
30613 e.preventDefault();
30615 this.fireEvent('click', this, o);
30619 closable : function(closable)
30621 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30623 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30635 xhrOnLoad : function(xhr)
30637 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30641 if (xhr.readyState !== 4) {
30643 this.fireEvent('exception', this, xhr);
30647 var response = Roo.decode(xhr.responseText);
30649 if(!response.success){
30651 this.fireEvent('exception', this, xhr);
30655 var file = this.renderPreview(response.data);
30657 this.files.push(file);
30661 this.fireEvent('afterupload', this, xhr);
30665 xhrOnError : function(xhr)
30667 Roo.log('xhr on error');
30669 var response = Roo.decode(xhr.responseText);
30676 process : function(file)
30678 if(this.fireEvent('process', this, file) !== false){
30679 if(this.editable && file.type.indexOf('image') != -1){
30680 this.fireEvent('edit', this, file);
30684 this.uploadStart(file, false);
30691 uploadStart : function(file, crop)
30693 this.xhr = new XMLHttpRequest();
30695 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30700 file.xhr = this.xhr;
30702 this.managerEl.createChild({
30704 cls : 'roo-document-manager-loading',
30708 tooltip : file.name,
30709 cls : 'roo-document-manager-thumb',
30710 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30716 this.xhr.open(this.method, this.url, true);
30719 "Accept": "application/json",
30720 "Cache-Control": "no-cache",
30721 "X-Requested-With": "XMLHttpRequest"
30724 for (var headerName in headers) {
30725 var headerValue = headers[headerName];
30727 this.xhr.setRequestHeader(headerName, headerValue);
30733 this.xhr.onload = function()
30735 _this.xhrOnLoad(_this.xhr);
30738 this.xhr.onerror = function()
30740 _this.xhrOnError(_this.xhr);
30743 var formData = new FormData();
30745 formData.append('returnHTML', 'NO');
30748 formData.append('crop', crop);
30751 formData.append(this.paramName, file, file.name);
30758 if(this.fireEvent('prepare', this, formData, options) != false){
30760 if(options.manually){
30764 this.xhr.send(formData);
30768 this.uploadCancel();
30771 uploadCancel : function()
30777 this.delegates = [];
30779 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30786 renderPreview : function(file)
30788 if(typeof(file.target) != 'undefined' && file.target){
30792 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30794 var previewEl = this.managerEl.createChild({
30796 cls : 'roo-document-manager-preview',
30800 tooltip : file[this.toolTipName],
30801 cls : 'roo-document-manager-thumb',
30802 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30807 html : '<i class="fa fa-times-circle"></i>'
30812 var close = previewEl.select('button.close', true).first();
30814 close.on('click', this.onRemove, this, file);
30816 file.target = previewEl;
30818 var image = previewEl.select('img', true).first();
30822 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30824 image.on('click', this.onClick, this, file);
30826 this.fireEvent('previewrendered', this, file);
30832 onPreviewLoad : function(file, image)
30834 if(typeof(file.target) == 'undefined' || !file.target){
30838 var width = image.dom.naturalWidth || image.dom.width;
30839 var height = image.dom.naturalHeight || image.dom.height;
30841 if(!this.previewResize) {
30845 if(width > height){
30846 file.target.addClass('wide');
30850 file.target.addClass('tall');
30855 uploadFromSource : function(file, crop)
30857 this.xhr = new XMLHttpRequest();
30859 this.managerEl.createChild({
30861 cls : 'roo-document-manager-loading',
30865 tooltip : file.name,
30866 cls : 'roo-document-manager-thumb',
30867 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30873 this.xhr.open(this.method, this.url, true);
30876 "Accept": "application/json",
30877 "Cache-Control": "no-cache",
30878 "X-Requested-With": "XMLHttpRequest"
30881 for (var headerName in headers) {
30882 var headerValue = headers[headerName];
30884 this.xhr.setRequestHeader(headerName, headerValue);
30890 this.xhr.onload = function()
30892 _this.xhrOnLoad(_this.xhr);
30895 this.xhr.onerror = function()
30897 _this.xhrOnError(_this.xhr);
30900 var formData = new FormData();
30902 formData.append('returnHTML', 'NO');
30904 formData.append('crop', crop);
30906 if(typeof(file.filename) != 'undefined'){
30907 formData.append('filename', file.filename);
30910 if(typeof(file.mimetype) != 'undefined'){
30911 formData.append('mimetype', file.mimetype);
30916 if(this.fireEvent('prepare', this, formData) != false){
30917 this.xhr.send(formData);
30927 * @class Roo.bootstrap.DocumentViewer
30928 * @extends Roo.bootstrap.Component
30929 * Bootstrap DocumentViewer class
30930 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30931 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30934 * Create a new DocumentViewer
30935 * @param {Object} config The config object
30938 Roo.bootstrap.DocumentViewer = function(config){
30939 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30944 * Fire after initEvent
30945 * @param {Roo.bootstrap.DocumentViewer} this
30951 * @param {Roo.bootstrap.DocumentViewer} this
30956 * Fire after download button
30957 * @param {Roo.bootstrap.DocumentViewer} this
30962 * Fire after trash button
30963 * @param {Roo.bootstrap.DocumentViewer} this
30970 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30972 showDownload : true,
30976 getAutoCreate : function()
30980 cls : 'roo-document-viewer',
30984 cls : 'roo-document-viewer-body',
30988 cls : 'roo-document-viewer-thumb',
30992 cls : 'roo-document-viewer-image'
31000 cls : 'roo-document-viewer-footer',
31003 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31007 cls : 'btn-group roo-document-viewer-download',
31011 cls : 'btn btn-default',
31012 html : '<i class="fa fa-download"></i>'
31018 cls : 'btn-group roo-document-viewer-trash',
31022 cls : 'btn btn-default',
31023 html : '<i class="fa fa-trash"></i>'
31036 initEvents : function()
31038 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31039 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31041 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31042 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31044 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31045 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31047 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31048 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31050 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31051 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31053 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31054 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31056 this.bodyEl.on('click', this.onClick, this);
31057 this.downloadBtn.on('click', this.onDownload, this);
31058 this.trashBtn.on('click', this.onTrash, this);
31060 this.downloadBtn.hide();
31061 this.trashBtn.hide();
31063 if(this.showDownload){
31064 this.downloadBtn.show();
31067 if(this.showTrash){
31068 this.trashBtn.show();
31071 if(!this.showDownload && !this.showTrash) {
31072 this.footerEl.hide();
31077 initial : function()
31079 this.fireEvent('initial', this);
31083 onClick : function(e)
31085 e.preventDefault();
31087 this.fireEvent('click', this);
31090 onDownload : function(e)
31092 e.preventDefault();
31094 this.fireEvent('download', this);
31097 onTrash : function(e)
31099 e.preventDefault();
31101 this.fireEvent('trash', this);
31113 * @class Roo.bootstrap.NavProgressBar
31114 * @extends Roo.bootstrap.Component
31115 * Bootstrap NavProgressBar class
31118 * Create a new nav progress bar
31119 * @param {Object} config The config object
31122 Roo.bootstrap.NavProgressBar = function(config){
31123 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31125 this.bullets = this.bullets || [];
31127 // Roo.bootstrap.NavProgressBar.register(this);
31131 * Fires when the active item changes
31132 * @param {Roo.bootstrap.NavProgressBar} this
31133 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31134 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31141 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31146 getAutoCreate : function()
31148 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31152 cls : 'roo-navigation-bar-group',
31156 cls : 'roo-navigation-top-bar'
31160 cls : 'roo-navigation-bullets-bar',
31164 cls : 'roo-navigation-bar'
31171 cls : 'roo-navigation-bottom-bar'
31181 initEvents: function()
31186 onRender : function(ct, position)
31188 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31190 if(this.bullets.length){
31191 Roo.each(this.bullets, function(b){
31200 addItem : function(cfg)
31202 var item = new Roo.bootstrap.NavProgressItem(cfg);
31204 item.parentId = this.id;
31205 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31208 var top = new Roo.bootstrap.Element({
31210 cls : 'roo-navigation-bar-text'
31213 var bottom = new Roo.bootstrap.Element({
31215 cls : 'roo-navigation-bar-text'
31218 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31219 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31221 var topText = new Roo.bootstrap.Element({
31223 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31226 var bottomText = new Roo.bootstrap.Element({
31228 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31231 topText.onRender(top.el, null);
31232 bottomText.onRender(bottom.el, null);
31235 item.bottomEl = bottom;
31238 this.barItems.push(item);
31243 getActive : function()
31245 var active = false;
31247 Roo.each(this.barItems, function(v){
31249 if (!v.isActive()) {
31261 setActiveItem : function(item)
31265 Roo.each(this.barItems, function(v){
31266 if (v.rid == item.rid) {
31270 if (v.isActive()) {
31271 v.setActive(false);
31276 item.setActive(true);
31278 this.fireEvent('changed', this, item, prev);
31281 getBarItem: function(rid)
31285 Roo.each(this.barItems, function(e) {
31286 if (e.rid != rid) {
31297 indexOfItem : function(item)
31301 Roo.each(this.barItems, function(v, i){
31303 if (v.rid != item.rid) {
31314 setActiveNext : function()
31316 var i = this.indexOfItem(this.getActive());
31318 if (i > this.barItems.length) {
31322 this.setActiveItem(this.barItems[i+1]);
31325 setActivePrev : function()
31327 var i = this.indexOfItem(this.getActive());
31333 this.setActiveItem(this.barItems[i-1]);
31336 format : function()
31338 if(!this.barItems.length){
31342 var width = 100 / this.barItems.length;
31344 Roo.each(this.barItems, function(i){
31345 i.el.setStyle('width', width + '%');
31346 i.topEl.el.setStyle('width', width + '%');
31347 i.bottomEl.el.setStyle('width', width + '%');
31356 * Nav Progress Item
31361 * @class Roo.bootstrap.NavProgressItem
31362 * @extends Roo.bootstrap.Component
31363 * Bootstrap NavProgressItem class
31364 * @cfg {String} rid the reference id
31365 * @cfg {Boolean} active (true|false) Is item active default false
31366 * @cfg {Boolean} disabled (true|false) Is item active default false
31367 * @cfg {String} html
31368 * @cfg {String} position (top|bottom) text position default bottom
31369 * @cfg {String} icon show icon instead of number
31372 * Create a new NavProgressItem
31373 * @param {Object} config The config object
31375 Roo.bootstrap.NavProgressItem = function(config){
31376 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31381 * The raw click event for the entire grid.
31382 * @param {Roo.bootstrap.NavProgressItem} this
31383 * @param {Roo.EventObject} e
31390 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
31396 position : 'bottom',
31399 getAutoCreate : function()
31401 var iconCls = 'roo-navigation-bar-item-icon';
31403 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31407 cls: 'roo-navigation-bar-item',
31417 cfg.cls += ' active';
31420 cfg.cls += ' disabled';
31426 disable : function()
31428 this.setDisabled(true);
31431 enable : function()
31433 this.setDisabled(false);
31436 initEvents: function()
31438 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31440 this.iconEl.on('click', this.onClick, this);
31443 onClick : function(e)
31445 e.preventDefault();
31451 if(this.fireEvent('click', this, e) === false){
31455 this.parent().setActiveItem(this);
31458 isActive: function ()
31460 return this.active;
31463 setActive : function(state)
31465 if(this.active == state){
31469 this.active = state;
31472 this.el.addClass('active');
31476 this.el.removeClass('active');
31481 setDisabled : function(state)
31483 if(this.disabled == state){
31487 this.disabled = state;
31490 this.el.addClass('disabled');
31494 this.el.removeClass('disabled');
31497 tooltipEl : function()
31499 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31512 * @class Roo.bootstrap.FieldLabel
31513 * @extends Roo.bootstrap.Component
31514 * Bootstrap FieldLabel class
31515 * @cfg {String} html contents of the element
31516 * @cfg {String} tag tag of the element default label
31517 * @cfg {String} cls class of the element
31518 * @cfg {String} target label target
31519 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31520 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31521 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31522 * @cfg {String} iconTooltip default "This field is required"
31523 * @cfg {String} indicatorpos (left|right) default left
31526 * Create a new FieldLabel
31527 * @param {Object} config The config object
31530 Roo.bootstrap.FieldLabel = function(config){
31531 Roo.bootstrap.Element.superclass.constructor.call(this, config);
31536 * Fires after the field has been marked as invalid.
31537 * @param {Roo.form.FieldLabel} this
31538 * @param {String} msg The validation message
31543 * Fires after the field has been validated with no errors.
31544 * @param {Roo.form.FieldLabel} this
31550 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
31557 invalidClass : 'has-warning',
31558 validClass : 'has-success',
31559 iconTooltip : 'This field is required',
31560 indicatorpos : 'left',
31562 getAutoCreate : function(){
31565 if (!this.allowBlank) {
31571 cls : 'roo-bootstrap-field-label ' + this.cls,
31576 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31577 tooltip : this.iconTooltip
31586 if(this.indicatorpos == 'right'){
31589 cls : 'roo-bootstrap-field-label ' + this.cls,
31598 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31599 tooltip : this.iconTooltip
31608 initEvents: function()
31610 Roo.bootstrap.Element.superclass.initEvents.call(this);
31612 this.indicator = this.indicatorEl();
31614 if(this.indicator){
31615 this.indicator.removeClass('visible');
31616 this.indicator.addClass('invisible');
31619 Roo.bootstrap.FieldLabel.register(this);
31622 indicatorEl : function()
31624 var indicator = this.el.select('i.roo-required-indicator',true).first();
31635 * Mark this field as valid
31637 markValid : function()
31639 if(this.indicator){
31640 this.indicator.removeClass('visible');
31641 this.indicator.addClass('invisible');
31643 if (Roo.bootstrap.version == 3) {
31644 this.el.removeClass(this.invalidClass);
31645 this.el.addClass(this.validClass);
31647 this.el.removeClass('is-invalid');
31648 this.el.addClass('is-valid');
31652 this.fireEvent('valid', this);
31656 * Mark this field as invalid
31657 * @param {String} msg The validation message
31659 markInvalid : function(msg)
31661 if(this.indicator){
31662 this.indicator.removeClass('invisible');
31663 this.indicator.addClass('visible');
31665 if (Roo.bootstrap.version == 3) {
31666 this.el.removeClass(this.validClass);
31667 this.el.addClass(this.invalidClass);
31669 this.el.removeClass('is-valid');
31670 this.el.addClass('is-invalid');
31674 this.fireEvent('invalid', this, msg);
31680 Roo.apply(Roo.bootstrap.FieldLabel, {
31685 * register a FieldLabel Group
31686 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31688 register : function(label)
31690 if(this.groups.hasOwnProperty(label.target)){
31694 this.groups[label.target] = label;
31698 * fetch a FieldLabel Group based on the target
31699 * @param {string} target
31700 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31702 get: function(target) {
31703 if (typeof(this.groups[target]) == 'undefined') {
31707 return this.groups[target] ;
31716 * page DateSplitField.
31722 * @class Roo.bootstrap.DateSplitField
31723 * @extends Roo.bootstrap.Component
31724 * Bootstrap DateSplitField class
31725 * @cfg {string} fieldLabel - the label associated
31726 * @cfg {Number} labelWidth set the width of label (0-12)
31727 * @cfg {String} labelAlign (top|left)
31728 * @cfg {Boolean} dayAllowBlank (true|false) default false
31729 * @cfg {Boolean} monthAllowBlank (true|false) default false
31730 * @cfg {Boolean} yearAllowBlank (true|false) default false
31731 * @cfg {string} dayPlaceholder
31732 * @cfg {string} monthPlaceholder
31733 * @cfg {string} yearPlaceholder
31734 * @cfg {string} dayFormat default 'd'
31735 * @cfg {string} monthFormat default 'm'
31736 * @cfg {string} yearFormat default 'Y'
31737 * @cfg {Number} labellg set the width of label (1-12)
31738 * @cfg {Number} labelmd set the width of label (1-12)
31739 * @cfg {Number} labelsm set the width of label (1-12)
31740 * @cfg {Number} labelxs set the width of label (1-12)
31744 * Create a new DateSplitField
31745 * @param {Object} config The config object
31748 Roo.bootstrap.DateSplitField = function(config){
31749 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31755 * getting the data of years
31756 * @param {Roo.bootstrap.DateSplitField} this
31757 * @param {Object} years
31762 * getting the data of days
31763 * @param {Roo.bootstrap.DateSplitField} this
31764 * @param {Object} days
31769 * Fires after the field has been marked as invalid.
31770 * @param {Roo.form.Field} this
31771 * @param {String} msg The validation message
31776 * Fires after the field has been validated with no errors.
31777 * @param {Roo.form.Field} this
31783 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31786 labelAlign : 'top',
31788 dayAllowBlank : false,
31789 monthAllowBlank : false,
31790 yearAllowBlank : false,
31791 dayPlaceholder : '',
31792 monthPlaceholder : '',
31793 yearPlaceholder : '',
31797 isFormField : true,
31803 getAutoCreate : function()
31807 cls : 'row roo-date-split-field-group',
31812 cls : 'form-hidden-field roo-date-split-field-group-value',
31818 var labelCls = 'col-md-12';
31819 var contentCls = 'col-md-4';
31821 if(this.fieldLabel){
31825 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31829 html : this.fieldLabel
31834 if(this.labelAlign == 'left'){
31836 if(this.labelWidth > 12){
31837 label.style = "width: " + this.labelWidth + 'px';
31840 if(this.labelWidth < 13 && this.labelmd == 0){
31841 this.labelmd = this.labelWidth;
31844 if(this.labellg > 0){
31845 labelCls = ' col-lg-' + this.labellg;
31846 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31849 if(this.labelmd > 0){
31850 labelCls = ' col-md-' + this.labelmd;
31851 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31854 if(this.labelsm > 0){
31855 labelCls = ' col-sm-' + this.labelsm;
31856 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31859 if(this.labelxs > 0){
31860 labelCls = ' col-xs-' + this.labelxs;
31861 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31865 label.cls += ' ' + labelCls;
31867 cfg.cn.push(label);
31870 Roo.each(['day', 'month', 'year'], function(t){
31873 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31880 inputEl: function ()
31882 return this.el.select('.roo-date-split-field-group-value', true).first();
31885 onRender : function(ct, position)
31889 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31891 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31893 this.dayField = new Roo.bootstrap.ComboBox({
31894 allowBlank : this.dayAllowBlank,
31895 alwaysQuery : true,
31896 displayField : 'value',
31899 forceSelection : true,
31901 placeholder : this.dayPlaceholder,
31902 selectOnFocus : true,
31903 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31904 triggerAction : 'all',
31906 valueField : 'value',
31907 store : new Roo.data.SimpleStore({
31908 data : (function() {
31910 _this.fireEvent('days', _this, days);
31913 fields : [ 'value' ]
31916 select : function (_self, record, index)
31918 _this.setValue(_this.getValue());
31923 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31925 this.monthField = new Roo.bootstrap.MonthField({
31926 after : '<i class=\"fa fa-calendar\"></i>',
31927 allowBlank : this.monthAllowBlank,
31928 placeholder : this.monthPlaceholder,
31931 render : function (_self)
31933 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31934 e.preventDefault();
31938 select : function (_self, oldvalue, newvalue)
31940 _this.setValue(_this.getValue());
31945 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31947 this.yearField = new Roo.bootstrap.ComboBox({
31948 allowBlank : this.yearAllowBlank,
31949 alwaysQuery : true,
31950 displayField : 'value',
31953 forceSelection : true,
31955 placeholder : this.yearPlaceholder,
31956 selectOnFocus : true,
31957 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31958 triggerAction : 'all',
31960 valueField : 'value',
31961 store : new Roo.data.SimpleStore({
31962 data : (function() {
31964 _this.fireEvent('years', _this, years);
31967 fields : [ 'value' ]
31970 select : function (_self, record, index)
31972 _this.setValue(_this.getValue());
31977 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31980 setValue : function(v, format)
31982 this.inputEl.dom.value = v;
31984 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31986 var d = Date.parseDate(v, f);
31993 this.setDay(d.format(this.dayFormat));
31994 this.setMonth(d.format(this.monthFormat));
31995 this.setYear(d.format(this.yearFormat));
32002 setDay : function(v)
32004 this.dayField.setValue(v);
32005 this.inputEl.dom.value = this.getValue();
32010 setMonth : function(v)
32012 this.monthField.setValue(v, true);
32013 this.inputEl.dom.value = this.getValue();
32018 setYear : function(v)
32020 this.yearField.setValue(v);
32021 this.inputEl.dom.value = this.getValue();
32026 getDay : function()
32028 return this.dayField.getValue();
32031 getMonth : function()
32033 return this.monthField.getValue();
32036 getYear : function()
32038 return this.yearField.getValue();
32041 getValue : function()
32043 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32045 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32055 this.inputEl.dom.value = '';
32060 validate : function()
32062 var d = this.dayField.validate();
32063 var m = this.monthField.validate();
32064 var y = this.yearField.validate();
32069 (!this.dayAllowBlank && !d) ||
32070 (!this.monthAllowBlank && !m) ||
32071 (!this.yearAllowBlank && !y)
32076 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32085 this.markInvalid();
32090 markValid : function()
32093 var label = this.el.select('label', true).first();
32094 var icon = this.el.select('i.fa-star', true).first();
32100 this.fireEvent('valid', this);
32104 * Mark this field as invalid
32105 * @param {String} msg The validation message
32107 markInvalid : function(msg)
32110 var label = this.el.select('label', true).first();
32111 var icon = this.el.select('i.fa-star', true).first();
32113 if(label && !icon){
32114 this.el.select('.roo-date-split-field-label', true).createChild({
32116 cls : 'text-danger fa fa-lg fa-star',
32117 tooltip : 'This field is required',
32118 style : 'margin-right:5px;'
32122 this.fireEvent('invalid', this, msg);
32125 clearInvalid : function()
32127 var label = this.el.select('label', true).first();
32128 var icon = this.el.select('i.fa-star', true).first();
32134 this.fireEvent('valid', this);
32137 getName: function()
32147 * http://masonry.desandro.com
32149 * The idea is to render all the bricks based on vertical width...
32151 * The original code extends 'outlayer' - we might need to use that....
32157 * @class Roo.bootstrap.LayoutMasonry
32158 * @extends Roo.bootstrap.Component
32159 * Bootstrap Layout Masonry class
32162 * Create a new Element
32163 * @param {Object} config The config object
32166 Roo.bootstrap.LayoutMasonry = function(config){
32168 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32172 Roo.bootstrap.LayoutMasonry.register(this);
32178 * Fire after layout the items
32179 * @param {Roo.bootstrap.LayoutMasonry} this
32180 * @param {Roo.EventObject} e
32187 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32190 * @cfg {Boolean} isLayoutInstant = no animation?
32192 isLayoutInstant : false, // needed?
32195 * @cfg {Number} boxWidth width of the columns
32200 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32205 * @cfg {Number} padWidth padding below box..
32210 * @cfg {Number} gutter gutter width..
32215 * @cfg {Number} maxCols maximum number of columns
32221 * @cfg {Boolean} isAutoInitial defalut true
32223 isAutoInitial : true,
32228 * @cfg {Boolean} isHorizontal defalut false
32230 isHorizontal : false,
32232 currentSize : null,
32238 bricks: null, //CompositeElement
32242 _isLayoutInited : false,
32244 // isAlternative : false, // only use for vertical layout...
32247 * @cfg {Number} alternativePadWidth padding below box..
32249 alternativePadWidth : 50,
32251 selectedBrick : [],
32253 getAutoCreate : function(){
32255 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32259 cls: 'blog-masonary-wrapper ' + this.cls,
32261 cls : 'mas-boxes masonary'
32268 getChildContainer: function( )
32270 if (this.boxesEl) {
32271 return this.boxesEl;
32274 this.boxesEl = this.el.select('.mas-boxes').first();
32276 return this.boxesEl;
32280 initEvents : function()
32284 if(this.isAutoInitial){
32285 Roo.log('hook children rendered');
32286 this.on('childrenrendered', function() {
32287 Roo.log('children rendered');
32293 initial : function()
32295 this.selectedBrick = [];
32297 this.currentSize = this.el.getBox(true);
32299 Roo.EventManager.onWindowResize(this.resize, this);
32301 if(!this.isAutoInitial){
32309 //this.layout.defer(500,this);
32313 resize : function()
32315 var cs = this.el.getBox(true);
32318 this.currentSize.width == cs.width &&
32319 this.currentSize.x == cs.x &&
32320 this.currentSize.height == cs.height &&
32321 this.currentSize.y == cs.y
32323 Roo.log("no change in with or X or Y");
32327 this.currentSize = cs;
32333 layout : function()
32335 this._resetLayout();
32337 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32339 this.layoutItems( isInstant );
32341 this._isLayoutInited = true;
32343 this.fireEvent('layout', this);
32347 _resetLayout : function()
32349 if(this.isHorizontal){
32350 this.horizontalMeasureColumns();
32354 this.verticalMeasureColumns();
32358 verticalMeasureColumns : function()
32360 this.getContainerWidth();
32362 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32363 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32367 var boxWidth = this.boxWidth + this.padWidth;
32369 if(this.containerWidth < this.boxWidth){
32370 boxWidth = this.containerWidth
32373 var containerWidth = this.containerWidth;
32375 var cols = Math.floor(containerWidth / boxWidth);
32377 this.cols = Math.max( cols, 1 );
32379 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32381 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32383 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32385 this.colWidth = boxWidth + avail - this.padWidth;
32387 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32388 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32391 horizontalMeasureColumns : function()
32393 this.getContainerWidth();
32395 var boxWidth = this.boxWidth;
32397 if(this.containerWidth < boxWidth){
32398 boxWidth = this.containerWidth;
32401 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32403 this.el.setHeight(boxWidth);
32407 getContainerWidth : function()
32409 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32412 layoutItems : function( isInstant )
32414 Roo.log(this.bricks);
32416 var items = Roo.apply([], this.bricks);
32418 if(this.isHorizontal){
32419 this._horizontalLayoutItems( items , isInstant );
32423 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32424 // this._verticalAlternativeLayoutItems( items , isInstant );
32428 this._verticalLayoutItems( items , isInstant );
32432 _verticalLayoutItems : function ( items , isInstant)
32434 if ( !items || !items.length ) {
32439 ['xs', 'xs', 'xs', 'tall'],
32440 ['xs', 'xs', 'tall'],
32441 ['xs', 'xs', 'sm'],
32442 ['xs', 'xs', 'xs'],
32448 ['sm', 'xs', 'xs'],
32452 ['tall', 'xs', 'xs', 'xs'],
32453 ['tall', 'xs', 'xs'],
32465 Roo.each(items, function(item, k){
32467 switch (item.size) {
32468 // these layouts take up a full box,
32479 boxes.push([item]);
32502 var filterPattern = function(box, length)
32510 var pattern = box.slice(0, length);
32514 Roo.each(pattern, function(i){
32515 format.push(i.size);
32518 Roo.each(standard, function(s){
32520 if(String(s) != String(format)){
32529 if(!match && length == 1){
32534 filterPattern(box, length - 1);
32538 queue.push(pattern);
32540 box = box.slice(length, box.length);
32542 filterPattern(box, 4);
32548 Roo.each(boxes, function(box, k){
32554 if(box.length == 1){
32559 filterPattern(box, 4);
32563 this._processVerticalLayoutQueue( queue, isInstant );
32567 // _verticalAlternativeLayoutItems : function( items , isInstant )
32569 // if ( !items || !items.length ) {
32573 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
32577 _horizontalLayoutItems : function ( items , isInstant)
32579 if ( !items || !items.length || items.length < 3) {
32585 var eItems = items.slice(0, 3);
32587 items = items.slice(3, items.length);
32590 ['xs', 'xs', 'xs', 'wide'],
32591 ['xs', 'xs', 'wide'],
32592 ['xs', 'xs', 'sm'],
32593 ['xs', 'xs', 'xs'],
32599 ['sm', 'xs', 'xs'],
32603 ['wide', 'xs', 'xs', 'xs'],
32604 ['wide', 'xs', 'xs'],
32617 Roo.each(items, function(item, k){
32619 switch (item.size) {
32630 boxes.push([item]);
32654 var filterPattern = function(box, length)
32662 var pattern = box.slice(0, length);
32666 Roo.each(pattern, function(i){
32667 format.push(i.size);
32670 Roo.each(standard, function(s){
32672 if(String(s) != String(format)){
32681 if(!match && length == 1){
32686 filterPattern(box, length - 1);
32690 queue.push(pattern);
32692 box = box.slice(length, box.length);
32694 filterPattern(box, 4);
32700 Roo.each(boxes, function(box, k){
32706 if(box.length == 1){
32711 filterPattern(box, 4);
32718 var pos = this.el.getBox(true);
32722 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32724 var hit_end = false;
32726 Roo.each(queue, function(box){
32730 Roo.each(box, function(b){
32732 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32742 Roo.each(box, function(b){
32744 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32747 mx = Math.max(mx, b.x);
32751 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32755 Roo.each(box, function(b){
32757 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32771 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32774 /** Sets position of item in DOM
32775 * @param {Element} item
32776 * @param {Number} x - horizontal position
32777 * @param {Number} y - vertical position
32778 * @param {Boolean} isInstant - disables transitions
32780 _processVerticalLayoutQueue : function( queue, isInstant )
32782 var pos = this.el.getBox(true);
32787 for (var i = 0; i < this.cols; i++){
32791 Roo.each(queue, function(box, k){
32793 var col = k % this.cols;
32795 Roo.each(box, function(b,kk){
32797 b.el.position('absolute');
32799 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32800 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32802 if(b.size == 'md-left' || b.size == 'md-right'){
32803 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32804 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32807 b.el.setWidth(width);
32808 b.el.setHeight(height);
32810 b.el.select('iframe',true).setSize(width,height);
32814 for (var i = 0; i < this.cols; i++){
32816 if(maxY[i] < maxY[col]){
32821 col = Math.min(col, i);
32825 x = pos.x + col * (this.colWidth + this.padWidth);
32829 var positions = [];
32831 switch (box.length){
32833 positions = this.getVerticalOneBoxColPositions(x, y, box);
32836 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32839 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32842 positions = this.getVerticalFourBoxColPositions(x, y, box);
32848 Roo.each(box, function(b,kk){
32850 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32852 var sz = b.el.getSize();
32854 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32862 for (var i = 0; i < this.cols; i++){
32863 mY = Math.max(mY, maxY[i]);
32866 this.el.setHeight(mY - pos.y);
32870 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32872 // var pos = this.el.getBox(true);
32875 // var maxX = pos.right;
32877 // var maxHeight = 0;
32879 // Roo.each(items, function(item, k){
32883 // item.el.position('absolute');
32885 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32887 // item.el.setWidth(width);
32889 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32891 // item.el.setHeight(height);
32894 // item.el.setXY([x, y], isInstant ? false : true);
32896 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32899 // y = y + height + this.alternativePadWidth;
32901 // maxHeight = maxHeight + height + this.alternativePadWidth;
32905 // this.el.setHeight(maxHeight);
32909 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32911 var pos = this.el.getBox(true);
32916 var maxX = pos.right;
32918 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32920 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32922 Roo.each(queue, function(box, k){
32924 Roo.each(box, function(b, kk){
32926 b.el.position('absolute');
32928 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32929 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32931 if(b.size == 'md-left' || b.size == 'md-right'){
32932 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32933 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32936 b.el.setWidth(width);
32937 b.el.setHeight(height);
32945 var positions = [];
32947 switch (box.length){
32949 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32952 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32955 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32958 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32964 Roo.each(box, function(b,kk){
32966 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32968 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32976 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32978 Roo.each(eItems, function(b,k){
32980 b.size = (k == 0) ? 'sm' : 'xs';
32981 b.x = (k == 0) ? 2 : 1;
32982 b.y = (k == 0) ? 2 : 1;
32984 b.el.position('absolute');
32986 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32988 b.el.setWidth(width);
32990 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32992 b.el.setHeight(height);
32996 var positions = [];
32999 x : maxX - this.unitWidth * 2 - this.gutter,
33004 x : maxX - this.unitWidth,
33005 y : minY + (this.unitWidth + this.gutter) * 2
33009 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33013 Roo.each(eItems, function(b,k){
33015 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33021 getVerticalOneBoxColPositions : function(x, y, box)
33025 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33027 if(box[0].size == 'md-left'){
33031 if(box[0].size == 'md-right'){
33036 x : x + (this.unitWidth + this.gutter) * rand,
33043 getVerticalTwoBoxColPositions : function(x, y, box)
33047 if(box[0].size == 'xs'){
33051 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33055 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33069 x : x + (this.unitWidth + this.gutter) * 2,
33070 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33077 getVerticalThreeBoxColPositions : function(x, y, box)
33081 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33089 x : x + (this.unitWidth + this.gutter) * 1,
33094 x : x + (this.unitWidth + this.gutter) * 2,
33102 if(box[0].size == 'xs' && box[1].size == 'xs'){
33111 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33115 x : x + (this.unitWidth + this.gutter) * 1,
33129 x : x + (this.unitWidth + this.gutter) * 2,
33134 x : x + (this.unitWidth + this.gutter) * 2,
33135 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33142 getVerticalFourBoxColPositions : function(x, y, box)
33146 if(box[0].size == 'xs'){
33155 y : y + (this.unitHeight + this.gutter) * 1
33160 y : y + (this.unitHeight + this.gutter) * 2
33164 x : x + (this.unitWidth + this.gutter) * 1,
33178 x : x + (this.unitWidth + this.gutter) * 2,
33183 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33184 y : y + (this.unitHeight + this.gutter) * 1
33188 x : x + (this.unitWidth + this.gutter) * 2,
33189 y : y + (this.unitWidth + this.gutter) * 2
33196 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33200 if(box[0].size == 'md-left'){
33202 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33209 if(box[0].size == 'md-right'){
33211 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33212 y : minY + (this.unitWidth + this.gutter) * 1
33218 var rand = Math.floor(Math.random() * (4 - box[0].y));
33221 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33222 y : minY + (this.unitWidth + this.gutter) * rand
33229 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33233 if(box[0].size == 'xs'){
33236 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33241 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33242 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33250 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33255 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33256 y : minY + (this.unitWidth + this.gutter) * 2
33263 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33267 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33270 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33275 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33276 y : minY + (this.unitWidth + this.gutter) * 1
33280 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33281 y : minY + (this.unitWidth + this.gutter) * 2
33288 if(box[0].size == 'xs' && box[1].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[2].x - this.gutter * (box[2].x - 1),
33302 y : minY + (this.unitWidth + this.gutter) * 1
33310 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33315 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33316 y : minY + (this.unitWidth + this.gutter) * 2
33320 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33321 y : minY + (this.unitWidth + this.gutter) * 2
33328 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33332 if(box[0].size == 'xs'){
33335 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33340 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33345 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),
33350 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33351 y : minY + (this.unitWidth + this.gutter) * 1
33359 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33364 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33365 y : minY + (this.unitWidth + this.gutter) * 2
33369 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33370 y : minY + (this.unitWidth + this.gutter) * 2
33374 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),
33375 y : minY + (this.unitWidth + this.gutter) * 2
33383 * remove a Masonry Brick
33384 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33386 removeBrick : function(brick_id)
33392 for (var i = 0; i<this.bricks.length; i++) {
33393 if (this.bricks[i].id == brick_id) {
33394 this.bricks.splice(i,1);
33395 this.el.dom.removeChild(Roo.get(brick_id).dom);
33402 * adds a Masonry Brick
33403 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33405 addBrick : function(cfg)
33407 var cn = new Roo.bootstrap.MasonryBrick(cfg);
33408 //this.register(cn);
33409 cn.parentId = this.id;
33410 cn.render(this.el);
33415 * register a Masonry Brick
33416 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33419 register : function(brick)
33421 this.bricks.push(brick);
33422 brick.masonryId = this.id;
33426 * clear all the Masonry Brick
33428 clearAll : function()
33431 //this.getChildContainer().dom.innerHTML = "";
33432 this.el.dom.innerHTML = '';
33435 getSelected : function()
33437 if (!this.selectedBrick) {
33441 return this.selectedBrick;
33445 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33449 * register a Masonry Layout
33450 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33453 register : function(layout)
33455 this.groups[layout.id] = layout;
33458 * fetch a Masonry Layout based on the masonry layout ID
33459 * @param {string} the masonry layout to add
33460 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33463 get: function(layout_id) {
33464 if (typeof(this.groups[layout_id]) == 'undefined') {
33467 return this.groups[layout_id] ;
33479 * http://masonry.desandro.com
33481 * The idea is to render all the bricks based on vertical width...
33483 * The original code extends 'outlayer' - we might need to use that....
33489 * @class Roo.bootstrap.LayoutMasonryAuto
33490 * @extends Roo.bootstrap.Component
33491 * Bootstrap Layout Masonry class
33494 * Create a new Element
33495 * @param {Object} config The config object
33498 Roo.bootstrap.LayoutMasonryAuto = function(config){
33499 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33502 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
33505 * @cfg {Boolean} isFitWidth - resize the width..
33507 isFitWidth : false, // options..
33509 * @cfg {Boolean} isOriginLeft = left align?
33511 isOriginLeft : true,
33513 * @cfg {Boolean} isOriginTop = top align?
33515 isOriginTop : false,
33517 * @cfg {Boolean} isLayoutInstant = no animation?
33519 isLayoutInstant : false, // needed?
33521 * @cfg {Boolean} isResizingContainer = not sure if this is used..
33523 isResizingContainer : true,
33525 * @cfg {Number} columnWidth width of the columns
33531 * @cfg {Number} maxCols maximum number of columns
33536 * @cfg {Number} padHeight padding below box..
33542 * @cfg {Boolean} isAutoInitial defalut true
33545 isAutoInitial : true,
33551 initialColumnWidth : 0,
33552 currentSize : null,
33554 colYs : null, // array.
33561 bricks: null, //CompositeElement
33562 cols : 0, // array?
33563 // element : null, // wrapped now this.el
33564 _isLayoutInited : null,
33567 getAutoCreate : function(){
33571 cls: 'blog-masonary-wrapper ' + this.cls,
33573 cls : 'mas-boxes masonary'
33580 getChildContainer: function( )
33582 if (this.boxesEl) {
33583 return this.boxesEl;
33586 this.boxesEl = this.el.select('.mas-boxes').first();
33588 return this.boxesEl;
33592 initEvents : function()
33596 if(this.isAutoInitial){
33597 Roo.log('hook children rendered');
33598 this.on('childrenrendered', function() {
33599 Roo.log('children rendered');
33606 initial : function()
33608 this.reloadItems();
33610 this.currentSize = this.el.getBox(true);
33612 /// was window resize... - let's see if this works..
33613 Roo.EventManager.onWindowResize(this.resize, this);
33615 if(!this.isAutoInitial){
33620 this.layout.defer(500,this);
33623 reloadItems: function()
33625 this.bricks = this.el.select('.masonry-brick', true);
33627 this.bricks.each(function(b) {
33628 //Roo.log(b.getSize());
33629 if (!b.attr('originalwidth')) {
33630 b.attr('originalwidth', b.getSize().width);
33635 Roo.log(this.bricks.elements.length);
33638 resize : function()
33641 var cs = this.el.getBox(true);
33643 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33644 Roo.log("no change in with or X");
33647 this.currentSize = cs;
33651 layout : function()
33654 this._resetLayout();
33655 //this._manageStamps();
33657 // don't animate first layout
33658 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33659 this.layoutItems( isInstant );
33661 // flag for initalized
33662 this._isLayoutInited = true;
33665 layoutItems : function( isInstant )
33667 //var items = this._getItemsForLayout( this.items );
33668 // original code supports filtering layout items.. we just ignore it..
33670 this._layoutItems( this.bricks , isInstant );
33672 this._postLayout();
33674 _layoutItems : function ( items , isInstant)
33676 //this.fireEvent( 'layout', this, items );
33679 if ( !items || !items.elements.length ) {
33680 // no items, emit event with empty array
33685 items.each(function(item) {
33686 Roo.log("layout item");
33688 // get x/y object from method
33689 var position = this._getItemLayoutPosition( item );
33691 position.item = item;
33692 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33693 queue.push( position );
33696 this._processLayoutQueue( queue );
33698 /** Sets position of item in DOM
33699 * @param {Element} item
33700 * @param {Number} x - horizontal position
33701 * @param {Number} y - vertical position
33702 * @param {Boolean} isInstant - disables transitions
33704 _processLayoutQueue : function( queue )
33706 for ( var i=0, len = queue.length; i < len; i++ ) {
33707 var obj = queue[i];
33708 obj.item.position('absolute');
33709 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33715 * Any logic you want to do after each layout,
33716 * i.e. size the container
33718 _postLayout : function()
33720 this.resizeContainer();
33723 resizeContainer : function()
33725 if ( !this.isResizingContainer ) {
33728 var size = this._getContainerSize();
33730 this.el.setSize(size.width,size.height);
33731 this.boxesEl.setSize(size.width,size.height);
33737 _resetLayout : function()
33739 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33740 this.colWidth = this.el.getWidth();
33741 //this.gutter = this.el.getWidth();
33743 this.measureColumns();
33749 this.colYs.push( 0 );
33755 measureColumns : function()
33757 this.getContainerWidth();
33758 // if columnWidth is 0, default to outerWidth of first item
33759 if ( !this.columnWidth ) {
33760 var firstItem = this.bricks.first();
33761 Roo.log(firstItem);
33762 this.columnWidth = this.containerWidth;
33763 if (firstItem && firstItem.attr('originalwidth') ) {
33764 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33766 // columnWidth fall back to item of first element
33767 Roo.log("set column width?");
33768 this.initialColumnWidth = this.columnWidth ;
33770 // if first elem has no width, default to size of container
33775 if (this.initialColumnWidth) {
33776 this.columnWidth = this.initialColumnWidth;
33781 // column width is fixed at the top - however if container width get's smaller we should
33784 // this bit calcs how man columns..
33786 var columnWidth = this.columnWidth += this.gutter;
33788 // calculate columns
33789 var containerWidth = this.containerWidth + this.gutter;
33791 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33792 // fix rounding errors, typically with gutters
33793 var excess = columnWidth - containerWidth % columnWidth;
33796 // if overshoot is less than a pixel, round up, otherwise floor it
33797 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33798 cols = Math[ mathMethod ]( cols );
33799 this.cols = Math.max( cols, 1 );
33800 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33802 // padding positioning..
33803 var totalColWidth = this.cols * this.columnWidth;
33804 var padavail = this.containerWidth - totalColWidth;
33805 // so for 2 columns - we need 3 'pads'
33807 var padNeeded = (1+this.cols) * this.padWidth;
33809 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33811 this.columnWidth += padExtra
33812 //this.padWidth = Math.floor(padavail / ( this.cols));
33814 // adjust colum width so that padding is fixed??
33816 // we have 3 columns ... total = width * 3
33817 // we have X left over... that should be used by
33819 //if (this.expandC) {
33827 getContainerWidth : function()
33829 /* // container is parent if fit width
33830 var container = this.isFitWidth ? this.element.parentNode : this.element;
33831 // check that this.size and size are there
33832 // IE8 triggers resize on body size change, so they might not be
33834 var size = getSize( container ); //FIXME
33835 this.containerWidth = size && size.innerWidth; //FIXME
33838 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33842 _getItemLayoutPosition : function( item ) // what is item?
33844 // we resize the item to our columnWidth..
33846 item.setWidth(this.columnWidth);
33847 item.autoBoxAdjust = false;
33849 var sz = item.getSize();
33851 // how many columns does this brick span
33852 var remainder = this.containerWidth % this.columnWidth;
33854 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33855 // round if off by 1 pixel, otherwise use ceil
33856 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33857 colSpan = Math.min( colSpan, this.cols );
33859 // normally this should be '1' as we dont' currently allow multi width columns..
33861 var colGroup = this._getColGroup( colSpan );
33862 // get the minimum Y value from the columns
33863 var minimumY = Math.min.apply( Math, colGroup );
33864 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33866 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33868 // position the brick
33870 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33871 y: this.currentSize.y + minimumY + this.padHeight
33875 // apply setHeight to necessary columns
33876 var setHeight = minimumY + sz.height + this.padHeight;
33877 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33879 var setSpan = this.cols + 1 - colGroup.length;
33880 for ( var i = 0; i < setSpan; i++ ) {
33881 this.colYs[ shortColIndex + i ] = setHeight ;
33888 * @param {Number} colSpan - number of columns the element spans
33889 * @returns {Array} colGroup
33891 _getColGroup : function( colSpan )
33893 if ( colSpan < 2 ) {
33894 // if brick spans only one column, use all the column Ys
33899 // how many different places could this brick fit horizontally
33900 var groupCount = this.cols + 1 - colSpan;
33901 // for each group potential horizontal position
33902 for ( var i = 0; i < groupCount; i++ ) {
33903 // make an array of colY values for that one group
33904 var groupColYs = this.colYs.slice( i, i + colSpan );
33905 // and get the max value of the array
33906 colGroup[i] = Math.max.apply( Math, groupColYs );
33911 _manageStamp : function( stamp )
33913 var stampSize = stamp.getSize();
33914 var offset = stamp.getBox();
33915 // get the columns that this stamp affects
33916 var firstX = this.isOriginLeft ? offset.x : offset.right;
33917 var lastX = firstX + stampSize.width;
33918 var firstCol = Math.floor( firstX / this.columnWidth );
33919 firstCol = Math.max( 0, firstCol );
33921 var lastCol = Math.floor( lastX / this.columnWidth );
33922 // lastCol should not go over if multiple of columnWidth #425
33923 lastCol -= lastX % this.columnWidth ? 0 : 1;
33924 lastCol = Math.min( this.cols - 1, lastCol );
33926 // set colYs to bottom of the stamp
33927 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33930 for ( var i = firstCol; i <= lastCol; i++ ) {
33931 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33936 _getContainerSize : function()
33938 this.maxY = Math.max.apply( Math, this.colYs );
33943 if ( this.isFitWidth ) {
33944 size.width = this._getContainerFitWidth();
33950 _getContainerFitWidth : function()
33952 var unusedCols = 0;
33953 // count unused columns
33956 if ( this.colYs[i] !== 0 ) {
33961 // fit container to columns that have been used
33962 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33965 needsResizeLayout : function()
33967 var previousWidth = this.containerWidth;
33968 this.getContainerWidth();
33969 return previousWidth !== this.containerWidth;
33984 * @class Roo.bootstrap.MasonryBrick
33985 * @extends Roo.bootstrap.Component
33986 * Bootstrap MasonryBrick class
33989 * Create a new MasonryBrick
33990 * @param {Object} config The config object
33993 Roo.bootstrap.MasonryBrick = function(config){
33995 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33997 Roo.bootstrap.MasonryBrick.register(this);
34003 * When a MasonryBrick is clcik
34004 * @param {Roo.bootstrap.MasonryBrick} this
34005 * @param {Roo.EventObject} e
34011 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34014 * @cfg {String} title
34018 * @cfg {String} html
34022 * @cfg {String} bgimage
34026 * @cfg {String} videourl
34030 * @cfg {String} cls
34034 * @cfg {String} href
34038 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34043 * @cfg {String} placetitle (center|bottom)
34048 * @cfg {Boolean} isFitContainer defalut true
34050 isFitContainer : true,
34053 * @cfg {Boolean} preventDefault defalut false
34055 preventDefault : false,
34058 * @cfg {Boolean} inverse defalut false
34060 maskInverse : false,
34062 getAutoCreate : function()
34064 if(!this.isFitContainer){
34065 return this.getSplitAutoCreate();
34068 var cls = 'masonry-brick masonry-brick-full';
34070 if(this.href.length){
34071 cls += ' masonry-brick-link';
34074 if(this.bgimage.length){
34075 cls += ' masonry-brick-image';
34078 if(this.maskInverse){
34079 cls += ' mask-inverse';
34082 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34083 cls += ' enable-mask';
34087 cls += ' masonry-' + this.size + '-brick';
34090 if(this.placetitle.length){
34092 switch (this.placetitle) {
34094 cls += ' masonry-center-title';
34097 cls += ' masonry-bottom-title';
34104 if(!this.html.length && !this.bgimage.length){
34105 cls += ' masonry-center-title';
34108 if(!this.html.length && this.bgimage.length){
34109 cls += ' masonry-bottom-title';
34114 cls += ' ' + this.cls;
34118 tag: (this.href.length) ? 'a' : 'div',
34123 cls: 'masonry-brick-mask'
34127 cls: 'masonry-brick-paragraph',
34133 if(this.href.length){
34134 cfg.href = this.href;
34137 var cn = cfg.cn[1].cn;
34139 if(this.title.length){
34142 cls: 'masonry-brick-title',
34147 if(this.html.length){
34150 cls: 'masonry-brick-text',
34155 if (!this.title.length && !this.html.length) {
34156 cfg.cn[1].cls += ' hide';
34159 if(this.bgimage.length){
34162 cls: 'masonry-brick-image-view',
34167 if(this.videourl.length){
34168 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34169 // youtube support only?
34172 cls: 'masonry-brick-image-view',
34175 allowfullscreen : true
34183 getSplitAutoCreate : function()
34185 var cls = 'masonry-brick masonry-brick-split';
34187 if(this.href.length){
34188 cls += ' masonry-brick-link';
34191 if(this.bgimage.length){
34192 cls += ' masonry-brick-image';
34196 cls += ' masonry-' + this.size + '-brick';
34199 switch (this.placetitle) {
34201 cls += ' masonry-center-title';
34204 cls += ' masonry-bottom-title';
34207 if(!this.bgimage.length){
34208 cls += ' masonry-center-title';
34211 if(this.bgimage.length){
34212 cls += ' masonry-bottom-title';
34218 cls += ' ' + this.cls;
34222 tag: (this.href.length) ? 'a' : 'div',
34227 cls: 'masonry-brick-split-head',
34231 cls: 'masonry-brick-paragraph',
34238 cls: 'masonry-brick-split-body',
34244 if(this.href.length){
34245 cfg.href = this.href;
34248 if(this.title.length){
34249 cfg.cn[0].cn[0].cn.push({
34251 cls: 'masonry-brick-title',
34256 if(this.html.length){
34257 cfg.cn[1].cn.push({
34259 cls: 'masonry-brick-text',
34264 if(this.bgimage.length){
34265 cfg.cn[0].cn.push({
34267 cls: 'masonry-brick-image-view',
34272 if(this.videourl.length){
34273 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34274 // youtube support only?
34275 cfg.cn[0].cn.cn.push({
34277 cls: 'masonry-brick-image-view',
34280 allowfullscreen : true
34287 initEvents: function()
34289 switch (this.size) {
34322 this.el.on('touchstart', this.onTouchStart, this);
34323 this.el.on('touchmove', this.onTouchMove, this);
34324 this.el.on('touchend', this.onTouchEnd, this);
34325 this.el.on('contextmenu', this.onContextMenu, this);
34327 this.el.on('mouseenter' ,this.enter, this);
34328 this.el.on('mouseleave', this.leave, this);
34329 this.el.on('click', this.onClick, this);
34332 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34333 this.parent().bricks.push(this);
34338 onClick: function(e, el)
34340 var time = this.endTimer - this.startTimer;
34341 // Roo.log(e.preventDefault());
34344 e.preventDefault();
34349 if(!this.preventDefault){
34353 e.preventDefault();
34355 if (this.activeClass != '') {
34356 this.selectBrick();
34359 this.fireEvent('click', this, e);
34362 enter: function(e, el)
34364 e.preventDefault();
34366 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34370 if(this.bgimage.length && this.html.length){
34371 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34375 leave: function(e, el)
34377 e.preventDefault();
34379 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34383 if(this.bgimage.length && this.html.length){
34384 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34388 onTouchStart: function(e, el)
34390 // e.preventDefault();
34392 this.touchmoved = false;
34394 if(!this.isFitContainer){
34398 if(!this.bgimage.length || !this.html.length){
34402 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34404 this.timer = new Date().getTime();
34408 onTouchMove: function(e, el)
34410 this.touchmoved = true;
34413 onContextMenu : function(e,el)
34415 e.preventDefault();
34416 e.stopPropagation();
34420 onTouchEnd: function(e, el)
34422 // e.preventDefault();
34424 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34431 if(!this.bgimage.length || !this.html.length){
34433 if(this.href.length){
34434 window.location.href = this.href;
34440 if(!this.isFitContainer){
34444 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34446 window.location.href = this.href;
34449 //selection on single brick only
34450 selectBrick : function() {
34452 if (!this.parentId) {
34456 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34457 var index = m.selectedBrick.indexOf(this.id);
34460 m.selectedBrick.splice(index,1);
34461 this.el.removeClass(this.activeClass);
34465 for(var i = 0; i < m.selectedBrick.length; i++) {
34466 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34467 b.el.removeClass(b.activeClass);
34470 m.selectedBrick = [];
34472 m.selectedBrick.push(this.id);
34473 this.el.addClass(this.activeClass);
34477 isSelected : function(){
34478 return this.el.hasClass(this.activeClass);
34483 Roo.apply(Roo.bootstrap.MasonryBrick, {
34486 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34488 * register a Masonry Brick
34489 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34492 register : function(brick)
34494 //this.groups[brick.id] = brick;
34495 this.groups.add(brick.id, brick);
34498 * fetch a masonry brick based on the masonry brick ID
34499 * @param {string} the masonry brick to add
34500 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34503 get: function(brick_id)
34505 // if (typeof(this.groups[brick_id]) == 'undefined') {
34508 // return this.groups[brick_id] ;
34510 if(this.groups.key(brick_id)) {
34511 return this.groups.key(brick_id);
34529 * @class Roo.bootstrap.Brick
34530 * @extends Roo.bootstrap.Component
34531 * Bootstrap Brick class
34534 * Create a new Brick
34535 * @param {Object} config The config object
34538 Roo.bootstrap.Brick = function(config){
34539 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34545 * When a Brick is click
34546 * @param {Roo.bootstrap.Brick} this
34547 * @param {Roo.EventObject} e
34553 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
34556 * @cfg {String} title
34560 * @cfg {String} html
34564 * @cfg {String} bgimage
34568 * @cfg {String} cls
34572 * @cfg {String} href
34576 * @cfg {String} video
34580 * @cfg {Boolean} square
34584 getAutoCreate : function()
34586 var cls = 'roo-brick';
34588 if(this.href.length){
34589 cls += ' roo-brick-link';
34592 if(this.bgimage.length){
34593 cls += ' roo-brick-image';
34596 if(!this.html.length && !this.bgimage.length){
34597 cls += ' roo-brick-center-title';
34600 if(!this.html.length && this.bgimage.length){
34601 cls += ' roo-brick-bottom-title';
34605 cls += ' ' + this.cls;
34609 tag: (this.href.length) ? 'a' : 'div',
34614 cls: 'roo-brick-paragraph',
34620 if(this.href.length){
34621 cfg.href = this.href;
34624 var cn = cfg.cn[0].cn;
34626 if(this.title.length){
34629 cls: 'roo-brick-title',
34634 if(this.html.length){
34637 cls: 'roo-brick-text',
34644 if(this.bgimage.length){
34647 cls: 'roo-brick-image-view',
34655 initEvents: function()
34657 if(this.title.length || this.html.length){
34658 this.el.on('mouseenter' ,this.enter, this);
34659 this.el.on('mouseleave', this.leave, this);
34662 Roo.EventManager.onWindowResize(this.resize, this);
34664 if(this.bgimage.length){
34665 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34666 this.imageEl.on('load', this.onImageLoad, this);
34673 onImageLoad : function()
34678 resize : function()
34680 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34682 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34684 if(this.bgimage.length){
34685 var image = this.el.select('.roo-brick-image-view', true).first();
34687 image.setWidth(paragraph.getWidth());
34690 image.setHeight(paragraph.getWidth());
34693 this.el.setHeight(image.getHeight());
34694 paragraph.setHeight(image.getHeight());
34700 enter: function(e, el)
34702 e.preventDefault();
34704 if(this.bgimage.length){
34705 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34706 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34710 leave: function(e, el)
34712 e.preventDefault();
34714 if(this.bgimage.length){
34715 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34716 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34731 * @class Roo.bootstrap.NumberField
34732 * @extends Roo.bootstrap.Input
34733 * Bootstrap NumberField class
34739 * Create a new NumberField
34740 * @param {Object} config The config object
34743 Roo.bootstrap.NumberField = function(config){
34744 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34747 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34750 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34752 allowDecimals : true,
34754 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34756 decimalSeparator : ".",
34758 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34760 decimalPrecision : 2,
34762 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34764 allowNegative : true,
34767 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34771 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34773 minValue : Number.NEGATIVE_INFINITY,
34775 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34777 maxValue : Number.MAX_VALUE,
34779 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34781 minText : "The minimum value for this field is {0}",
34783 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34785 maxText : "The maximum value for this field is {0}",
34787 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34788 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34790 nanText : "{0} is not a valid number",
34792 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34794 thousandsDelimiter : false,
34796 * @cfg {String} valueAlign alignment of value
34798 valueAlign : "left",
34800 getAutoCreate : function()
34802 var hiddenInput = {
34806 cls: 'hidden-number-input'
34810 hiddenInput.name = this.name;
34815 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34817 this.name = hiddenInput.name;
34819 if(cfg.cn.length > 0) {
34820 cfg.cn.push(hiddenInput);
34827 initEvents : function()
34829 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34831 var allowed = "0123456789";
34833 if(this.allowDecimals){
34834 allowed += this.decimalSeparator;
34837 if(this.allowNegative){
34841 if(this.thousandsDelimiter) {
34845 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34847 var keyPress = function(e){
34849 var k = e.getKey();
34851 var c = e.getCharCode();
34854 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34855 allowed.indexOf(String.fromCharCode(c)) === -1
34861 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34865 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34870 this.el.on("keypress", keyPress, this);
34873 validateValue : function(value)
34876 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34880 var num = this.parseValue(value);
34883 this.markInvalid(String.format(this.nanText, value));
34887 if(num < this.minValue){
34888 this.markInvalid(String.format(this.minText, this.minValue));
34892 if(num > this.maxValue){
34893 this.markInvalid(String.format(this.maxText, this.maxValue));
34900 getValue : function()
34902 var v = this.hiddenEl().getValue();
34904 return this.fixPrecision(this.parseValue(v));
34907 parseValue : function(value)
34909 if(this.thousandsDelimiter) {
34911 r = new RegExp(",", "g");
34912 value = value.replace(r, "");
34915 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34916 return isNaN(value) ? '' : value;
34919 fixPrecision : function(value)
34921 if(this.thousandsDelimiter) {
34923 r = new RegExp(",", "g");
34924 value = value.replace(r, "");
34927 var nan = isNaN(value);
34929 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34930 return nan ? '' : value;
34932 return parseFloat(value).toFixed(this.decimalPrecision);
34935 setValue : function(v)
34937 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34943 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34945 this.inputEl().dom.value = (v == '') ? '' :
34946 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34948 if(!this.allowZero && v === '0') {
34949 this.hiddenEl().dom.value = '';
34950 this.inputEl().dom.value = '';
34957 decimalPrecisionFcn : function(v)
34959 return Math.floor(v);
34962 beforeBlur : function()
34964 var v = this.parseValue(this.getRawValue());
34966 if(v || v === 0 || v === ''){
34971 hiddenEl : function()
34973 return this.el.select('input.hidden-number-input',true).first();
34985 * @class Roo.bootstrap.DocumentSlider
34986 * @extends Roo.bootstrap.Component
34987 * Bootstrap DocumentSlider class
34990 * Create a new DocumentViewer
34991 * @param {Object} config The config object
34994 Roo.bootstrap.DocumentSlider = function(config){
34995 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35002 * Fire after initEvent
35003 * @param {Roo.bootstrap.DocumentSlider} this
35008 * Fire after update
35009 * @param {Roo.bootstrap.DocumentSlider} this
35015 * @param {Roo.bootstrap.DocumentSlider} this
35021 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35027 getAutoCreate : function()
35031 cls : 'roo-document-slider',
35035 cls : 'roo-document-slider-header',
35039 cls : 'roo-document-slider-header-title'
35045 cls : 'roo-document-slider-body',
35049 cls : 'roo-document-slider-prev',
35053 cls : 'fa fa-chevron-left'
35059 cls : 'roo-document-slider-thumb',
35063 cls : 'roo-document-slider-image'
35069 cls : 'roo-document-slider-next',
35073 cls : 'fa fa-chevron-right'
35085 initEvents : function()
35087 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35088 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35090 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35091 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35093 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35094 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35096 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35097 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35099 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35100 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35102 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35103 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35105 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35106 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35108 this.thumbEl.on('click', this.onClick, this);
35110 this.prevIndicator.on('click', this.prev, this);
35112 this.nextIndicator.on('click', this.next, this);
35116 initial : function()
35118 if(this.files.length){
35119 this.indicator = 1;
35123 this.fireEvent('initial', this);
35126 update : function()
35128 this.imageEl.attr('src', this.files[this.indicator - 1]);
35130 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35132 this.prevIndicator.show();
35134 if(this.indicator == 1){
35135 this.prevIndicator.hide();
35138 this.nextIndicator.show();
35140 if(this.indicator == this.files.length){
35141 this.nextIndicator.hide();
35144 this.thumbEl.scrollTo('top');
35146 this.fireEvent('update', this);
35149 onClick : function(e)
35151 e.preventDefault();
35153 this.fireEvent('click', this);
35158 e.preventDefault();
35160 this.indicator = Math.max(1, this.indicator - 1);
35167 e.preventDefault();
35169 this.indicator = Math.min(this.files.length, this.indicator + 1);
35183 * @class Roo.bootstrap.RadioSet
35184 * @extends Roo.bootstrap.Input
35185 * Bootstrap RadioSet class
35186 * @cfg {String} indicatorpos (left|right) default left
35187 * @cfg {Boolean} inline (true|false) inline the element (default true)
35188 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35190 * Create a new RadioSet
35191 * @param {Object} config The config object
35194 Roo.bootstrap.RadioSet = function(config){
35196 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35200 Roo.bootstrap.RadioSet.register(this);
35205 * Fires when the element is checked or unchecked.
35206 * @param {Roo.bootstrap.RadioSet} this This radio
35207 * @param {Roo.bootstrap.Radio} item The checked item
35212 * Fires when the element is click.
35213 * @param {Roo.bootstrap.RadioSet} this This radio set
35214 * @param {Roo.bootstrap.Radio} item The checked item
35215 * @param {Roo.EventObject} e The event object
35222 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35230 indicatorpos : 'left',
35232 getAutoCreate : function()
35236 cls : 'roo-radio-set-label',
35240 html : this.fieldLabel
35244 if (Roo.bootstrap.version == 3) {
35247 if(this.indicatorpos == 'left'){
35250 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35251 tooltip : 'This field is required'
35256 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35257 tooltip : 'This field is required'
35263 cls : 'roo-radio-set-items'
35266 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35268 if (align === 'left' && this.fieldLabel.length) {
35271 cls : "roo-radio-set-right",
35277 if(this.labelWidth > 12){
35278 label.style = "width: " + this.labelWidth + 'px';
35281 if(this.labelWidth < 13 && this.labelmd == 0){
35282 this.labelmd = this.labelWidth;
35285 if(this.labellg > 0){
35286 label.cls += ' col-lg-' + this.labellg;
35287 items.cls += ' col-lg-' + (12 - this.labellg);
35290 if(this.labelmd > 0){
35291 label.cls += ' col-md-' + this.labelmd;
35292 items.cls += ' col-md-' + (12 - this.labelmd);
35295 if(this.labelsm > 0){
35296 label.cls += ' col-sm-' + this.labelsm;
35297 items.cls += ' col-sm-' + (12 - this.labelsm);
35300 if(this.labelxs > 0){
35301 label.cls += ' col-xs-' + this.labelxs;
35302 items.cls += ' col-xs-' + (12 - this.labelxs);
35308 cls : 'roo-radio-set',
35312 cls : 'roo-radio-set-input',
35315 value : this.value ? this.value : ''
35322 if(this.weight.length){
35323 cfg.cls += ' roo-radio-' + this.weight;
35327 cfg.cls += ' roo-radio-set-inline';
35331 ['xs','sm','md','lg'].map(function(size){
35332 if (settings[size]) {
35333 cfg.cls += ' col-' + size + '-' + settings[size];
35341 initEvents : function()
35343 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35344 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35346 if(!this.fieldLabel.length){
35347 this.labelEl.hide();
35350 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35351 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35353 this.indicator = this.indicatorEl();
35355 if(this.indicator){
35356 this.indicator.addClass('invisible');
35359 this.originalValue = this.getValue();
35363 inputEl: function ()
35365 return this.el.select('.roo-radio-set-input', true).first();
35368 getChildContainer : function()
35370 return this.itemsEl;
35373 register : function(item)
35375 this.radioes.push(item);
35379 validate : function()
35381 if(this.getVisibilityEl().hasClass('hidden')){
35387 Roo.each(this.radioes, function(i){
35396 if(this.allowBlank) {
35400 if(this.disabled || valid){
35405 this.markInvalid();
35410 markValid : function()
35412 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35413 this.indicatorEl().removeClass('visible');
35414 this.indicatorEl().addClass('invisible');
35418 if (Roo.bootstrap.version == 3) {
35419 this.el.removeClass([this.invalidClass, this.validClass]);
35420 this.el.addClass(this.validClass);
35422 this.el.removeClass(['is-invalid','is-valid']);
35423 this.el.addClass(['is-valid']);
35425 this.fireEvent('valid', this);
35428 markInvalid : function(msg)
35430 if(this.allowBlank || this.disabled){
35434 if(this.labelEl.isVisible(true) && this.indicatorEl()){
35435 this.indicatorEl().removeClass('invisible');
35436 this.indicatorEl().addClass('visible');
35438 if (Roo.bootstrap.version == 3) {
35439 this.el.removeClass([this.invalidClass, this.validClass]);
35440 this.el.addClass(this.invalidClass);
35442 this.el.removeClass(['is-invalid','is-valid']);
35443 this.el.addClass(['is-invalid']);
35446 this.fireEvent('invalid', this, msg);
35450 setValue : function(v, suppressEvent)
35452 if(this.value === v){
35459 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35462 Roo.each(this.radioes, function(i){
35464 i.el.removeClass('checked');
35467 Roo.each(this.radioes, function(i){
35469 if(i.value === v || i.value.toString() === v.toString()){
35471 i.el.addClass('checked');
35473 if(suppressEvent !== true){
35474 this.fireEvent('check', this, i);
35485 clearInvalid : function(){
35487 if(!this.el || this.preventMark){
35491 this.el.removeClass([this.invalidClass]);
35493 this.fireEvent('valid', this);
35498 Roo.apply(Roo.bootstrap.RadioSet, {
35502 register : function(set)
35504 this.groups[set.name] = set;
35507 get: function(name)
35509 if (typeof(this.groups[name]) == 'undefined') {
35513 return this.groups[name] ;
35519 * Ext JS Library 1.1.1
35520 * Copyright(c) 2006-2007, Ext JS, LLC.
35522 * Originally Released Under LGPL - original licence link has changed is not relivant.
35525 * <script type="text/javascript">
35530 * @class Roo.bootstrap.SplitBar
35531 * @extends Roo.util.Observable
35532 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35536 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35537 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35538 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35539 split.minSize = 100;
35540 split.maxSize = 600;
35541 split.animate = true;
35542 split.on('moved', splitterMoved);
35545 * Create a new SplitBar
35546 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
35547 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
35548 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35549 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
35550 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35551 position of the SplitBar).
35553 Roo.bootstrap.SplitBar = function(cfg){
35558 // dragElement : elm
35559 // resizingElement: el,
35561 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35562 // placement : Roo.bootstrap.SplitBar.LEFT ,
35563 // existingProxy ???
35566 this.el = Roo.get(cfg.dragElement, true);
35567 this.el.dom.unselectable = "on";
35569 this.resizingEl = Roo.get(cfg.resizingElement, true);
35573 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35574 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35577 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35580 * The minimum size of the resizing element. (Defaults to 0)
35586 * The maximum size of the resizing element. (Defaults to 2000)
35589 this.maxSize = 2000;
35592 * Whether to animate the transition to the new size
35595 this.animate = false;
35598 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35601 this.useShim = false;
35606 if(!cfg.existingProxy){
35608 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35610 this.proxy = Roo.get(cfg.existingProxy).dom;
35613 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35616 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35619 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35622 this.dragSpecs = {};
35625 * @private The adapter to use to positon and resize elements
35627 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35628 this.adapter.init(this);
35630 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35632 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35633 this.el.addClass("roo-splitbar-h");
35636 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35637 this.el.addClass("roo-splitbar-v");
35643 * Fires when the splitter is moved (alias for {@link #event-moved})
35644 * @param {Roo.bootstrap.SplitBar} this
35645 * @param {Number} newSize the new width or height
35650 * Fires when the splitter is moved
35651 * @param {Roo.bootstrap.SplitBar} this
35652 * @param {Number} newSize the new width or height
35656 * @event beforeresize
35657 * Fires before the splitter is dragged
35658 * @param {Roo.bootstrap.SplitBar} this
35660 "beforeresize" : true,
35662 "beforeapply" : true
35665 Roo.util.Observable.call(this);
35668 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35669 onStartProxyDrag : function(x, y){
35670 this.fireEvent("beforeresize", this);
35672 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35674 o.enableDisplayMode("block");
35675 // all splitbars share the same overlay
35676 Roo.bootstrap.SplitBar.prototype.overlay = o;
35678 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35679 this.overlay.show();
35680 Roo.get(this.proxy).setDisplayed("block");
35681 var size = this.adapter.getElementSize(this);
35682 this.activeMinSize = this.getMinimumSize();;
35683 this.activeMaxSize = this.getMaximumSize();;
35684 var c1 = size - this.activeMinSize;
35685 var c2 = Math.max(this.activeMaxSize - size, 0);
35686 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35687 this.dd.resetConstraints();
35688 this.dd.setXConstraint(
35689 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35690 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35692 this.dd.setYConstraint(0, 0);
35694 this.dd.resetConstraints();
35695 this.dd.setXConstraint(0, 0);
35696 this.dd.setYConstraint(
35697 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35698 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35701 this.dragSpecs.startSize = size;
35702 this.dragSpecs.startPoint = [x, y];
35703 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35707 * @private Called after the drag operation by the DDProxy
35709 onEndProxyDrag : function(e){
35710 Roo.get(this.proxy).setDisplayed(false);
35711 var endPoint = Roo.lib.Event.getXY(e);
35713 this.overlay.hide();
35716 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35717 newSize = this.dragSpecs.startSize +
35718 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35719 endPoint[0] - this.dragSpecs.startPoint[0] :
35720 this.dragSpecs.startPoint[0] - endPoint[0]
35723 newSize = this.dragSpecs.startSize +
35724 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35725 endPoint[1] - this.dragSpecs.startPoint[1] :
35726 this.dragSpecs.startPoint[1] - endPoint[1]
35729 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35730 if(newSize != this.dragSpecs.startSize){
35731 if(this.fireEvent('beforeapply', this, newSize) !== false){
35732 this.adapter.setElementSize(this, newSize);
35733 this.fireEvent("moved", this, newSize);
35734 this.fireEvent("resize", this, newSize);
35740 * Get the adapter this SplitBar uses
35741 * @return The adapter object
35743 getAdapter : function(){
35744 return this.adapter;
35748 * Set the adapter this SplitBar uses
35749 * @param {Object} adapter A SplitBar adapter object
35751 setAdapter : function(adapter){
35752 this.adapter = adapter;
35753 this.adapter.init(this);
35757 * Gets the minimum size for the resizing element
35758 * @return {Number} The minimum size
35760 getMinimumSize : function(){
35761 return this.minSize;
35765 * Sets the minimum size for the resizing element
35766 * @param {Number} minSize The minimum size
35768 setMinimumSize : function(minSize){
35769 this.minSize = minSize;
35773 * Gets the maximum size for the resizing element
35774 * @return {Number} The maximum size
35776 getMaximumSize : function(){
35777 return this.maxSize;
35781 * Sets the maximum size for the resizing element
35782 * @param {Number} maxSize The maximum size
35784 setMaximumSize : function(maxSize){
35785 this.maxSize = maxSize;
35789 * Sets the initialize size for the resizing element
35790 * @param {Number} size The initial size
35792 setCurrentSize : function(size){
35793 var oldAnimate = this.animate;
35794 this.animate = false;
35795 this.adapter.setElementSize(this, size);
35796 this.animate = oldAnimate;
35800 * Destroy this splitbar.
35801 * @param {Boolean} removeEl True to remove the element
35803 destroy : function(removeEl){
35805 this.shim.remove();
35808 this.proxy.parentNode.removeChild(this.proxy);
35816 * @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.
35818 Roo.bootstrap.SplitBar.createProxy = function(dir){
35819 var proxy = new Roo.Element(document.createElement("div"));
35820 proxy.unselectable();
35821 var cls = 'roo-splitbar-proxy';
35822 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35823 document.body.appendChild(proxy.dom);
35828 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35829 * Default Adapter. It assumes the splitter and resizing element are not positioned
35830 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35832 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35835 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35836 // do nothing for now
35837 init : function(s){
35841 * Called before drag operations to get the current size of the resizing element.
35842 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35844 getElementSize : function(s){
35845 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35846 return s.resizingEl.getWidth();
35848 return s.resizingEl.getHeight();
35853 * Called after drag operations to set the size of the resizing element.
35854 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35855 * @param {Number} newSize The new size to set
35856 * @param {Function} onComplete A function to be invoked when resizing is complete
35858 setElementSize : function(s, newSize, onComplete){
35859 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35861 s.resizingEl.setWidth(newSize);
35863 onComplete(s, newSize);
35866 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35871 s.resizingEl.setHeight(newSize);
35873 onComplete(s, newSize);
35876 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35883 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35884 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35885 * Adapter that moves the splitter element to align with the resized sizing element.
35886 * Used with an absolute positioned SplitBar.
35887 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35888 * document.body, make sure you assign an id to the body element.
35890 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35891 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35892 this.container = Roo.get(container);
35895 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35896 init : function(s){
35897 this.basic.init(s);
35900 getElementSize : function(s){
35901 return this.basic.getElementSize(s);
35904 setElementSize : function(s, newSize, onComplete){
35905 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35908 moveSplitter : function(s){
35909 var yes = Roo.bootstrap.SplitBar;
35910 switch(s.placement){
35912 s.el.setX(s.resizingEl.getRight());
35915 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35918 s.el.setY(s.resizingEl.getBottom());
35921 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35928 * Orientation constant - Create a vertical SplitBar
35932 Roo.bootstrap.SplitBar.VERTICAL = 1;
35935 * Orientation constant - Create a horizontal SplitBar
35939 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35942 * Placement constant - The resizing element is to the left of the splitter element
35946 Roo.bootstrap.SplitBar.LEFT = 1;
35949 * Placement constant - The resizing element is to the right of the splitter element
35953 Roo.bootstrap.SplitBar.RIGHT = 2;
35956 * Placement constant - The resizing element is positioned above the splitter element
35960 Roo.bootstrap.SplitBar.TOP = 3;
35963 * Placement constant - The resizing element is positioned under splitter element
35967 Roo.bootstrap.SplitBar.BOTTOM = 4;
35968 Roo.namespace("Roo.bootstrap.layout");/*
35970 * Ext JS Library 1.1.1
35971 * Copyright(c) 2006-2007, Ext JS, LLC.
35973 * Originally Released Under LGPL - original licence link has changed is not relivant.
35976 * <script type="text/javascript">
35980 * @class Roo.bootstrap.layout.Manager
35981 * @extends Roo.bootstrap.Component
35982 * Base class for layout managers.
35984 Roo.bootstrap.layout.Manager = function(config)
35986 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35992 /** false to disable window resize monitoring @type Boolean */
35993 this.monitorWindowResize = true;
35998 * Fires when a layout is performed.
35999 * @param {Roo.LayoutManager} this
36003 * @event regionresized
36004 * Fires when the user resizes a region.
36005 * @param {Roo.LayoutRegion} region The resized region
36006 * @param {Number} newSize The new size (width for east/west, height for north/south)
36008 "regionresized" : true,
36010 * @event regioncollapsed
36011 * Fires when a region is collapsed.
36012 * @param {Roo.LayoutRegion} region The collapsed region
36014 "regioncollapsed" : true,
36016 * @event regionexpanded
36017 * Fires when a region is expanded.
36018 * @param {Roo.LayoutRegion} region The expanded region
36020 "regionexpanded" : true
36022 this.updating = false;
36025 this.el = Roo.get(config.el);
36031 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36036 monitorWindowResize : true,
36042 onRender : function(ct, position)
36045 this.el = Roo.get(ct);
36048 //this.fireEvent('render',this);
36052 initEvents: function()
36056 // ie scrollbar fix
36057 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36058 document.body.scroll = "no";
36059 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36060 this.el.position('relative');
36062 this.id = this.el.id;
36063 this.el.addClass("roo-layout-container");
36064 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36065 if(this.el.dom != document.body ) {
36066 this.el.on('resize', this.layout,this);
36067 this.el.on('show', this.layout,this);
36073 * Returns true if this layout is currently being updated
36074 * @return {Boolean}
36076 isUpdating : function(){
36077 return this.updating;
36081 * Suspend the LayoutManager from doing auto-layouts while
36082 * making multiple add or remove calls
36084 beginUpdate : function(){
36085 this.updating = true;
36089 * Restore auto-layouts and optionally disable the manager from performing a layout
36090 * @param {Boolean} noLayout true to disable a layout update
36092 endUpdate : function(noLayout){
36093 this.updating = false;
36099 layout: function(){
36103 onRegionResized : function(region, newSize){
36104 this.fireEvent("regionresized", region, newSize);
36108 onRegionCollapsed : function(region){
36109 this.fireEvent("regioncollapsed", region);
36112 onRegionExpanded : function(region){
36113 this.fireEvent("regionexpanded", region);
36117 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36118 * performs box-model adjustments.
36119 * @return {Object} The size as an object {width: (the width), height: (the height)}
36121 getViewSize : function()
36124 if(this.el.dom != document.body){
36125 size = this.el.getSize();
36127 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36129 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36130 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36135 * Returns the Element this layout is bound to.
36136 * @return {Roo.Element}
36138 getEl : function(){
36143 * Returns the specified region.
36144 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36145 * @return {Roo.LayoutRegion}
36147 getRegion : function(target){
36148 return this.regions[target.toLowerCase()];
36151 onWindowResize : function(){
36152 if(this.monitorWindowResize){
36159 * Ext JS Library 1.1.1
36160 * Copyright(c) 2006-2007, Ext JS, LLC.
36162 * Originally Released Under LGPL - original licence link has changed is not relivant.
36165 * <script type="text/javascript">
36168 * @class Roo.bootstrap.layout.Border
36169 * @extends Roo.bootstrap.layout.Manager
36170 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36171 * please see: examples/bootstrap/nested.html<br><br>
36173 <b>The container the layout is rendered into can be either the body element or any other element.
36174 If it is not the body element, the container needs to either be an absolute positioned element,
36175 or you will need to add "position:relative" to the css of the container. You will also need to specify
36176 the container size if it is not the body element.</b>
36179 * Create a new Border
36180 * @param {Object} config Configuration options
36182 Roo.bootstrap.layout.Border = function(config){
36183 config = config || {};
36184 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36188 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36189 if(config[region]){
36190 config[region].region = region;
36191 this.addRegion(config[region]);
36197 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36199 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36201 parent : false, // this might point to a 'nest' or a ???
36204 * Creates and adds a new region if it doesn't already exist.
36205 * @param {String} target The target region key (north, south, east, west or center).
36206 * @param {Object} config The regions config object
36207 * @return {BorderLayoutRegion} The new region
36209 addRegion : function(config)
36211 if(!this.regions[config.region]){
36212 var r = this.factory(config);
36213 this.bindRegion(r);
36215 return this.regions[config.region];
36219 bindRegion : function(r){
36220 this.regions[r.config.region] = r;
36222 r.on("visibilitychange", this.layout, this);
36223 r.on("paneladded", this.layout, this);
36224 r.on("panelremoved", this.layout, this);
36225 r.on("invalidated", this.layout, this);
36226 r.on("resized", this.onRegionResized, this);
36227 r.on("collapsed", this.onRegionCollapsed, this);
36228 r.on("expanded", this.onRegionExpanded, this);
36232 * Performs a layout update.
36234 layout : function()
36236 if(this.updating) {
36240 // render all the rebions if they have not been done alreayd?
36241 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36242 if(this.regions[region] && !this.regions[region].bodyEl){
36243 this.regions[region].onRender(this.el)
36247 var size = this.getViewSize();
36248 var w = size.width;
36249 var h = size.height;
36254 //var x = 0, y = 0;
36256 var rs = this.regions;
36257 var north = rs["north"];
36258 var south = rs["south"];
36259 var west = rs["west"];
36260 var east = rs["east"];
36261 var center = rs["center"];
36262 //if(this.hideOnLayout){ // not supported anymore
36263 //c.el.setStyle("display", "none");
36265 if(north && north.isVisible()){
36266 var b = north.getBox();
36267 var m = north.getMargins();
36268 b.width = w - (m.left+m.right);
36271 centerY = b.height + b.y + m.bottom;
36272 centerH -= centerY;
36273 north.updateBox(this.safeBox(b));
36275 if(south && south.isVisible()){
36276 var b = south.getBox();
36277 var m = south.getMargins();
36278 b.width = w - (m.left+m.right);
36280 var totalHeight = (b.height + m.top + m.bottom);
36281 b.y = h - totalHeight + m.top;
36282 centerH -= totalHeight;
36283 south.updateBox(this.safeBox(b));
36285 if(west && west.isVisible()){
36286 var b = west.getBox();
36287 var m = west.getMargins();
36288 b.height = centerH - (m.top+m.bottom);
36290 b.y = centerY + m.top;
36291 var totalWidth = (b.width + m.left + m.right);
36292 centerX += totalWidth;
36293 centerW -= totalWidth;
36294 west.updateBox(this.safeBox(b));
36296 if(east && east.isVisible()){
36297 var b = east.getBox();
36298 var m = east.getMargins();
36299 b.height = centerH - (m.top+m.bottom);
36300 var totalWidth = (b.width + m.left + m.right);
36301 b.x = w - totalWidth + m.left;
36302 b.y = centerY + m.top;
36303 centerW -= totalWidth;
36304 east.updateBox(this.safeBox(b));
36307 var m = center.getMargins();
36309 x: centerX + m.left,
36310 y: centerY + m.top,
36311 width: centerW - (m.left+m.right),
36312 height: centerH - (m.top+m.bottom)
36314 //if(this.hideOnLayout){
36315 //center.el.setStyle("display", "block");
36317 center.updateBox(this.safeBox(centerBox));
36320 this.fireEvent("layout", this);
36324 safeBox : function(box){
36325 box.width = Math.max(0, box.width);
36326 box.height = Math.max(0, box.height);
36331 * Adds a ContentPanel (or subclass) to this layout.
36332 * @param {String} target The target region key (north, south, east, west or center).
36333 * @param {Roo.ContentPanel} panel The panel to add
36334 * @return {Roo.ContentPanel} The added panel
36336 add : function(target, panel){
36338 target = target.toLowerCase();
36339 return this.regions[target].add(panel);
36343 * Remove a ContentPanel (or subclass) to this layout.
36344 * @param {String} target The target region key (north, south, east, west or center).
36345 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36346 * @return {Roo.ContentPanel} The removed panel
36348 remove : function(target, panel){
36349 target = target.toLowerCase();
36350 return this.regions[target].remove(panel);
36354 * Searches all regions for a panel with the specified id
36355 * @param {String} panelId
36356 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36358 findPanel : function(panelId){
36359 var rs = this.regions;
36360 for(var target in rs){
36361 if(typeof rs[target] != "function"){
36362 var p = rs[target].getPanel(panelId);
36372 * Searches all regions for a panel with the specified id and activates (shows) it.
36373 * @param {String/ContentPanel} panelId The panels id or the panel itself
36374 * @return {Roo.ContentPanel} The shown panel or null
36376 showPanel : function(panelId) {
36377 var rs = this.regions;
36378 for(var target in rs){
36379 var r = rs[target];
36380 if(typeof r != "function"){
36381 if(r.hasPanel(panelId)){
36382 return r.showPanel(panelId);
36390 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36391 * @param {Roo.state.Provider} provider (optional) An alternate state provider
36394 restoreState : function(provider){
36396 provider = Roo.state.Manager;
36398 var sm = new Roo.LayoutStateManager();
36399 sm.init(this, provider);
36405 * Adds a xtype elements to the layout.
36409 xtype : 'ContentPanel',
36416 xtype : 'NestedLayoutPanel',
36422 items : [ ... list of content panels or nested layout panels.. ]
36426 * @param {Object} cfg Xtype definition of item to add.
36428 addxtype : function(cfg)
36430 // basically accepts a pannel...
36431 // can accept a layout region..!?!?
36432 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36435 // theory? children can only be panels??
36437 //if (!cfg.xtype.match(/Panel$/)) {
36442 if (typeof(cfg.region) == 'undefined') {
36443 Roo.log("Failed to add Panel, region was not set");
36447 var region = cfg.region;
36453 xitems = cfg.items;
36458 if ( region == 'center') {
36459 Roo.log("Center: " + cfg.title);
36465 case 'Content': // ContentPanel (el, cfg)
36466 case 'Scroll': // ContentPanel (el, cfg)
36468 cfg.autoCreate = cfg.autoCreate || true;
36469 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36471 // var el = this.el.createChild();
36472 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36475 this.add(region, ret);
36479 case 'TreePanel': // our new panel!
36480 cfg.el = this.el.createChild();
36481 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36482 this.add(region, ret);
36487 // create a new Layout (which is a Border Layout...
36489 var clayout = cfg.layout;
36490 clayout.el = this.el.createChild();
36491 clayout.items = clayout.items || [];
36495 // replace this exitems with the clayout ones..
36496 xitems = clayout.items;
36498 // force background off if it's in center...
36499 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36500 cfg.background = false;
36502 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
36505 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36506 //console.log('adding nested layout panel ' + cfg.toSource());
36507 this.add(region, ret);
36508 nb = {}; /// find first...
36513 // needs grid and region
36515 //var el = this.getRegion(region).el.createChild();
36517 *var el = this.el.createChild();
36518 // create the grid first...
36519 cfg.grid.container = el;
36520 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36523 if (region == 'center' && this.active ) {
36524 cfg.background = false;
36527 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36529 this.add(region, ret);
36531 if (cfg.background) {
36532 // render grid on panel activation (if panel background)
36533 ret.on('activate', function(gp) {
36534 if (!gp.grid.rendered) {
36535 // gp.grid.render(el);
36539 // cfg.grid.render(el);
36545 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36546 // it was the old xcomponent building that caused this before.
36547 // espeically if border is the top element in the tree.
36557 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36559 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36560 this.add(region, ret);
36564 throw "Can not add '" + cfg.xtype + "' to Border";
36570 this.beginUpdate();
36574 Roo.each(xitems, function(i) {
36575 region = nb && i.region ? i.region : false;
36577 var add = ret.addxtype(i);
36580 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36581 if (!i.background) {
36582 abn[region] = nb[region] ;
36589 // make the last non-background panel active..
36590 //if (nb) { Roo.log(abn); }
36593 for(var r in abn) {
36594 region = this.getRegion(r);
36596 // tried using nb[r], but it does not work..
36598 region.showPanel(abn[r]);
36609 factory : function(cfg)
36612 var validRegions = Roo.bootstrap.layout.Border.regions;
36614 var target = cfg.region;
36617 var r = Roo.bootstrap.layout;
36621 return new r.North(cfg);
36623 return new r.South(cfg);
36625 return new r.East(cfg);
36627 return new r.West(cfg);
36629 return new r.Center(cfg);
36631 throw 'Layout region "'+target+'" not supported.';
36638 * Ext JS Library 1.1.1
36639 * Copyright(c) 2006-2007, Ext JS, LLC.
36641 * Originally Released Under LGPL - original licence link has changed is not relivant.
36644 * <script type="text/javascript">
36648 * @class Roo.bootstrap.layout.Basic
36649 * @extends Roo.util.Observable
36650 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36651 * and does not have a titlebar, tabs or any other features. All it does is size and position
36652 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36653 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36654 * @cfg {string} region the region that it inhabits..
36655 * @cfg {bool} skipConfig skip config?
36659 Roo.bootstrap.layout.Basic = function(config){
36661 this.mgr = config.mgr;
36663 this.position = config.region;
36665 var skipConfig = config.skipConfig;
36669 * @scope Roo.BasicLayoutRegion
36673 * @event beforeremove
36674 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36675 * @param {Roo.LayoutRegion} this
36676 * @param {Roo.ContentPanel} panel The panel
36677 * @param {Object} e The cancel event object
36679 "beforeremove" : true,
36681 * @event invalidated
36682 * Fires when the layout for this region is changed.
36683 * @param {Roo.LayoutRegion} this
36685 "invalidated" : true,
36687 * @event visibilitychange
36688 * Fires when this region is shown or hidden
36689 * @param {Roo.LayoutRegion} this
36690 * @param {Boolean} visibility true or false
36692 "visibilitychange" : true,
36694 * @event paneladded
36695 * Fires when a panel is added.
36696 * @param {Roo.LayoutRegion} this
36697 * @param {Roo.ContentPanel} panel The panel
36699 "paneladded" : true,
36701 * @event panelremoved
36702 * Fires when a panel is removed.
36703 * @param {Roo.LayoutRegion} this
36704 * @param {Roo.ContentPanel} panel The panel
36706 "panelremoved" : true,
36708 * @event beforecollapse
36709 * Fires when this region before collapse.
36710 * @param {Roo.LayoutRegion} this
36712 "beforecollapse" : true,
36715 * Fires when this region is collapsed.
36716 * @param {Roo.LayoutRegion} this
36718 "collapsed" : true,
36721 * Fires when this region is expanded.
36722 * @param {Roo.LayoutRegion} this
36727 * Fires when this region is slid into view.
36728 * @param {Roo.LayoutRegion} this
36730 "slideshow" : true,
36733 * Fires when this region slides out of view.
36734 * @param {Roo.LayoutRegion} this
36736 "slidehide" : true,
36738 * @event panelactivated
36739 * Fires when a panel is activated.
36740 * @param {Roo.LayoutRegion} this
36741 * @param {Roo.ContentPanel} panel The activated panel
36743 "panelactivated" : true,
36746 * Fires when the user resizes this region.
36747 * @param {Roo.LayoutRegion} this
36748 * @param {Number} newSize The new size (width for east/west, height for north/south)
36752 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36753 this.panels = new Roo.util.MixedCollection();
36754 this.panels.getKey = this.getPanelId.createDelegate(this);
36756 this.activePanel = null;
36757 // ensure listeners are added...
36759 if (config.listeners || config.events) {
36760 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36761 listeners : config.listeners || {},
36762 events : config.events || {}
36766 if(skipConfig !== true){
36767 this.applyConfig(config);
36771 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36773 getPanelId : function(p){
36777 applyConfig : function(config){
36778 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36779 this.config = config;
36784 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36785 * the width, for horizontal (north, south) the height.
36786 * @param {Number} newSize The new width or height
36788 resizeTo : function(newSize){
36789 var el = this.el ? this.el :
36790 (this.activePanel ? this.activePanel.getEl() : null);
36792 switch(this.position){
36795 el.setWidth(newSize);
36796 this.fireEvent("resized", this, newSize);
36800 el.setHeight(newSize);
36801 this.fireEvent("resized", this, newSize);
36807 getBox : function(){
36808 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36811 getMargins : function(){
36812 return this.margins;
36815 updateBox : function(box){
36817 var el = this.activePanel.getEl();
36818 el.dom.style.left = box.x + "px";
36819 el.dom.style.top = box.y + "px";
36820 this.activePanel.setSize(box.width, box.height);
36824 * Returns the container element for this region.
36825 * @return {Roo.Element}
36827 getEl : function(){
36828 return this.activePanel;
36832 * Returns true if this region is currently visible.
36833 * @return {Boolean}
36835 isVisible : function(){
36836 return this.activePanel ? true : false;
36839 setActivePanel : function(panel){
36840 panel = this.getPanel(panel);
36841 if(this.activePanel && this.activePanel != panel){
36842 this.activePanel.setActiveState(false);
36843 this.activePanel.getEl().setLeftTop(-10000,-10000);
36845 this.activePanel = panel;
36846 panel.setActiveState(true);
36848 panel.setSize(this.box.width, this.box.height);
36850 this.fireEvent("panelactivated", this, panel);
36851 this.fireEvent("invalidated");
36855 * Show the specified panel.
36856 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36857 * @return {Roo.ContentPanel} The shown panel or null
36859 showPanel : function(panel){
36860 panel = this.getPanel(panel);
36862 this.setActivePanel(panel);
36868 * Get the active panel for this region.
36869 * @return {Roo.ContentPanel} The active panel or null
36871 getActivePanel : function(){
36872 return this.activePanel;
36876 * Add the passed ContentPanel(s)
36877 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36878 * @return {Roo.ContentPanel} The panel added (if only one was added)
36880 add : function(panel){
36881 if(arguments.length > 1){
36882 for(var i = 0, len = arguments.length; i < len; i++) {
36883 this.add(arguments[i]);
36887 if(this.hasPanel(panel)){
36888 this.showPanel(panel);
36891 var el = panel.getEl();
36892 if(el.dom.parentNode != this.mgr.el.dom){
36893 this.mgr.el.dom.appendChild(el.dom);
36895 if(panel.setRegion){
36896 panel.setRegion(this);
36898 this.panels.add(panel);
36899 el.setStyle("position", "absolute");
36900 if(!panel.background){
36901 this.setActivePanel(panel);
36902 if(this.config.initialSize && this.panels.getCount()==1){
36903 this.resizeTo(this.config.initialSize);
36906 this.fireEvent("paneladded", this, panel);
36911 * Returns true if the panel is in this region.
36912 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36913 * @return {Boolean}
36915 hasPanel : function(panel){
36916 if(typeof panel == "object"){ // must be panel obj
36917 panel = panel.getId();
36919 return this.getPanel(panel) ? true : false;
36923 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36924 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36925 * @param {Boolean} preservePanel Overrides the config preservePanel option
36926 * @return {Roo.ContentPanel} The panel that was removed
36928 remove : function(panel, preservePanel){
36929 panel = this.getPanel(panel);
36934 this.fireEvent("beforeremove", this, panel, e);
36935 if(e.cancel === true){
36938 var panelId = panel.getId();
36939 this.panels.removeKey(panelId);
36944 * Returns the panel specified or null if it's not in this region.
36945 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36946 * @return {Roo.ContentPanel}
36948 getPanel : function(id){
36949 if(typeof id == "object"){ // must be panel obj
36952 return this.panels.get(id);
36956 * Returns this regions position (north/south/east/west/center).
36959 getPosition: function(){
36960 return this.position;
36964 * Ext JS Library 1.1.1
36965 * Copyright(c) 2006-2007, Ext JS, LLC.
36967 * Originally Released Under LGPL - original licence link has changed is not relivant.
36970 * <script type="text/javascript">
36974 * @class Roo.bootstrap.layout.Region
36975 * @extends Roo.bootstrap.layout.Basic
36976 * This class represents a region in a layout manager.
36978 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36979 * @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})
36980 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36981 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36982 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36983 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36984 * @cfg {String} title The title for the region (overrides panel titles)
36985 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36986 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36987 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36988 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36989 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36990 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36991 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36992 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36993 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36994 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36996 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36997 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36998 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36999 * @cfg {Number} width For East/West panels
37000 * @cfg {Number} height For North/South panels
37001 * @cfg {Boolean} split To show the splitter
37002 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37004 * @cfg {string} cls Extra CSS classes to add to region
37006 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37007 * @cfg {string} region the region that it inhabits..
37010 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37011 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37013 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37014 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37015 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37017 Roo.bootstrap.layout.Region = function(config)
37019 this.applyConfig(config);
37021 var mgr = config.mgr;
37022 var pos = config.region;
37023 config.skipConfig = true;
37024 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37027 this.onRender(mgr.el);
37030 this.visible = true;
37031 this.collapsed = false;
37032 this.unrendered_panels = [];
37035 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37037 position: '', // set by wrapper (eg. north/south etc..)
37038 unrendered_panels : null, // unrendered panels.
37040 tabPosition : false,
37042 mgr: false, // points to 'Border'
37045 createBody : function(){
37046 /** This region's body element
37047 * @type Roo.Element */
37048 this.bodyEl = this.el.createChild({
37050 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37054 onRender: function(ctr, pos)
37056 var dh = Roo.DomHelper;
37057 /** This region's container element
37058 * @type Roo.Element */
37059 this.el = dh.append(ctr.dom, {
37061 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37063 /** This region's title element
37064 * @type Roo.Element */
37066 this.titleEl = dh.append(this.el.dom, {
37068 unselectable: "on",
37069 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37071 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37072 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37076 this.titleEl.enableDisplayMode();
37077 /** This region's title text element
37078 * @type HTMLElement */
37079 this.titleTextEl = this.titleEl.dom.firstChild;
37080 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37082 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37083 this.closeBtn.enableDisplayMode();
37084 this.closeBtn.on("click", this.closeClicked, this);
37085 this.closeBtn.hide();
37087 this.createBody(this.config);
37088 if(this.config.hideWhenEmpty){
37090 this.on("paneladded", this.validateVisibility, this);
37091 this.on("panelremoved", this.validateVisibility, this);
37093 if(this.autoScroll){
37094 this.bodyEl.setStyle("overflow", "auto");
37096 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37098 //if(c.titlebar !== false){
37099 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37100 this.titleEl.hide();
37102 this.titleEl.show();
37103 if(this.config.title){
37104 this.titleTextEl.innerHTML = this.config.title;
37108 if(this.config.collapsed){
37109 this.collapse(true);
37111 if(this.config.hidden){
37115 if (this.unrendered_panels && this.unrendered_panels.length) {
37116 for (var i =0;i< this.unrendered_panels.length; i++) {
37117 this.add(this.unrendered_panels[i]);
37119 this.unrendered_panels = null;
37125 applyConfig : function(c)
37128 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37129 var dh = Roo.DomHelper;
37130 if(c.titlebar !== false){
37131 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37132 this.collapseBtn.on("click", this.collapse, this);
37133 this.collapseBtn.enableDisplayMode();
37135 if(c.showPin === true || this.showPin){
37136 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37137 this.stickBtn.enableDisplayMode();
37138 this.stickBtn.on("click", this.expand, this);
37139 this.stickBtn.hide();
37144 /** This region's collapsed element
37145 * @type Roo.Element */
37148 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37149 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37152 if(c.floatable !== false){
37153 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37154 this.collapsedEl.on("click", this.collapseClick, this);
37157 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37158 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37159 id: "message", unselectable: "on", style:{"float":"left"}});
37160 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37162 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37163 this.expandBtn.on("click", this.expand, this);
37167 if(this.collapseBtn){
37168 this.collapseBtn.setVisible(c.collapsible == true);
37171 this.cmargins = c.cmargins || this.cmargins ||
37172 (this.position == "west" || this.position == "east" ?
37173 {top: 0, left: 2, right:2, bottom: 0} :
37174 {top: 2, left: 0, right:0, bottom: 2});
37176 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37179 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37181 this.autoScroll = c.autoScroll || false;
37186 this.duration = c.duration || .30;
37187 this.slideDuration = c.slideDuration || .45;
37192 * Returns true if this region is currently visible.
37193 * @return {Boolean}
37195 isVisible : function(){
37196 return this.visible;
37200 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37201 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37203 //setCollapsedTitle : function(title){
37204 // title = title || " ";
37205 // if(this.collapsedTitleTextEl){
37206 // this.collapsedTitleTextEl.innerHTML = title;
37210 getBox : function(){
37212 // if(!this.collapsed){
37213 b = this.el.getBox(false, true);
37215 // b = this.collapsedEl.getBox(false, true);
37220 getMargins : function(){
37221 return this.margins;
37222 //return this.collapsed ? this.cmargins : this.margins;
37225 highlight : function(){
37226 this.el.addClass("x-layout-panel-dragover");
37229 unhighlight : function(){
37230 this.el.removeClass("x-layout-panel-dragover");
37233 updateBox : function(box)
37235 if (!this.bodyEl) {
37236 return; // not rendered yet..
37240 if(!this.collapsed){
37241 this.el.dom.style.left = box.x + "px";
37242 this.el.dom.style.top = box.y + "px";
37243 this.updateBody(box.width, box.height);
37245 this.collapsedEl.dom.style.left = box.x + "px";
37246 this.collapsedEl.dom.style.top = box.y + "px";
37247 this.collapsedEl.setSize(box.width, box.height);
37250 this.tabs.autoSizeTabs();
37254 updateBody : function(w, h)
37257 this.el.setWidth(w);
37258 w -= this.el.getBorderWidth("rl");
37259 if(this.config.adjustments){
37260 w += this.config.adjustments[0];
37263 if(h !== null && h > 0){
37264 this.el.setHeight(h);
37265 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37266 h -= this.el.getBorderWidth("tb");
37267 if(this.config.adjustments){
37268 h += this.config.adjustments[1];
37270 this.bodyEl.setHeight(h);
37272 h = this.tabs.syncHeight(h);
37275 if(this.panelSize){
37276 w = w !== null ? w : this.panelSize.width;
37277 h = h !== null ? h : this.panelSize.height;
37279 if(this.activePanel){
37280 var el = this.activePanel.getEl();
37281 w = w !== null ? w : el.getWidth();
37282 h = h !== null ? h : el.getHeight();
37283 this.panelSize = {width: w, height: h};
37284 this.activePanel.setSize(w, h);
37286 if(Roo.isIE && this.tabs){
37287 this.tabs.el.repaint();
37292 * Returns the container element for this region.
37293 * @return {Roo.Element}
37295 getEl : function(){
37300 * Hides this region.
37303 //if(!this.collapsed){
37304 this.el.dom.style.left = "-2000px";
37307 // this.collapsedEl.dom.style.left = "-2000px";
37308 // this.collapsedEl.hide();
37310 this.visible = false;
37311 this.fireEvent("visibilitychange", this, false);
37315 * Shows this region if it was previously hidden.
37318 //if(!this.collapsed){
37321 // this.collapsedEl.show();
37323 this.visible = true;
37324 this.fireEvent("visibilitychange", this, true);
37327 closeClicked : function(){
37328 if(this.activePanel){
37329 this.remove(this.activePanel);
37333 collapseClick : function(e){
37335 e.stopPropagation();
37338 e.stopPropagation();
37344 * Collapses this region.
37345 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37348 collapse : function(skipAnim, skipCheck = false){
37349 if(this.collapsed) {
37353 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37355 this.collapsed = true;
37357 this.split.el.hide();
37359 if(this.config.animate && skipAnim !== true){
37360 this.fireEvent("invalidated", this);
37361 this.animateCollapse();
37363 this.el.setLocation(-20000,-20000);
37365 this.collapsedEl.show();
37366 this.fireEvent("collapsed", this);
37367 this.fireEvent("invalidated", this);
37373 animateCollapse : function(){
37378 * Expands this region if it was previously collapsed.
37379 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37380 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37383 expand : function(e, skipAnim){
37385 e.stopPropagation();
37387 if(!this.collapsed || this.el.hasActiveFx()) {
37391 this.afterSlideIn();
37394 this.collapsed = false;
37395 if(this.config.animate && skipAnim !== true){
37396 this.animateExpand();
37400 this.split.el.show();
37402 this.collapsedEl.setLocation(-2000,-2000);
37403 this.collapsedEl.hide();
37404 this.fireEvent("invalidated", this);
37405 this.fireEvent("expanded", this);
37409 animateExpand : function(){
37413 initTabs : function()
37415 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37417 var ts = new Roo.bootstrap.panel.Tabs({
37418 el: this.bodyEl.dom,
37420 tabPosition: this.tabPosition ? this.tabPosition : 'top',
37421 disableTooltips: this.config.disableTabTips,
37422 toolbar : this.config.toolbar
37425 if(this.config.hideTabs){
37426 ts.stripWrap.setDisplayed(false);
37429 ts.resizeTabs = this.config.resizeTabs === true;
37430 ts.minTabWidth = this.config.minTabWidth || 40;
37431 ts.maxTabWidth = this.config.maxTabWidth || 250;
37432 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37433 ts.monitorResize = false;
37434 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37435 ts.bodyEl.addClass('roo-layout-tabs-body');
37436 this.panels.each(this.initPanelAsTab, this);
37439 initPanelAsTab : function(panel){
37440 var ti = this.tabs.addTab(
37444 this.config.closeOnTab && panel.isClosable(),
37447 if(panel.tabTip !== undefined){
37448 ti.setTooltip(panel.tabTip);
37450 ti.on("activate", function(){
37451 this.setActivePanel(panel);
37454 if(this.config.closeOnTab){
37455 ti.on("beforeclose", function(t, e){
37457 this.remove(panel);
37461 panel.tabItem = ti;
37466 updatePanelTitle : function(panel, title)
37468 if(this.activePanel == panel){
37469 this.updateTitle(title);
37472 var ti = this.tabs.getTab(panel.getEl().id);
37474 if(panel.tabTip !== undefined){
37475 ti.setTooltip(panel.tabTip);
37480 updateTitle : function(title){
37481 if(this.titleTextEl && !this.config.title){
37482 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
37486 setActivePanel : function(panel)
37488 panel = this.getPanel(panel);
37489 if(this.activePanel && this.activePanel != panel){
37490 if(this.activePanel.setActiveState(false) === false){
37494 this.activePanel = panel;
37495 panel.setActiveState(true);
37496 if(this.panelSize){
37497 panel.setSize(this.panelSize.width, this.panelSize.height);
37500 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37502 this.updateTitle(panel.getTitle());
37504 this.fireEvent("invalidated", this);
37506 this.fireEvent("panelactivated", this, panel);
37510 * Shows the specified panel.
37511 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37512 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37514 showPanel : function(panel)
37516 panel = this.getPanel(panel);
37519 var tab = this.tabs.getTab(panel.getEl().id);
37520 if(tab.isHidden()){
37521 this.tabs.unhideTab(tab.id);
37525 this.setActivePanel(panel);
37532 * Get the active panel for this region.
37533 * @return {Roo.ContentPanel} The active panel or null
37535 getActivePanel : function(){
37536 return this.activePanel;
37539 validateVisibility : function(){
37540 if(this.panels.getCount() < 1){
37541 this.updateTitle(" ");
37542 this.closeBtn.hide();
37545 if(!this.isVisible()){
37552 * Adds the passed ContentPanel(s) to this region.
37553 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37554 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37556 add : function(panel)
37558 if(arguments.length > 1){
37559 for(var i = 0, len = arguments.length; i < len; i++) {
37560 this.add(arguments[i]);
37565 // if we have not been rendered yet, then we can not really do much of this..
37566 if (!this.bodyEl) {
37567 this.unrendered_panels.push(panel);
37574 if(this.hasPanel(panel)){
37575 this.showPanel(panel);
37578 panel.setRegion(this);
37579 this.panels.add(panel);
37580 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37581 // sinle panel - no tab...?? would it not be better to render it with the tabs,
37582 // and hide them... ???
37583 this.bodyEl.dom.appendChild(panel.getEl().dom);
37584 if(panel.background !== true){
37585 this.setActivePanel(panel);
37587 this.fireEvent("paneladded", this, panel);
37594 this.initPanelAsTab(panel);
37598 if(panel.background !== true){
37599 this.tabs.activate(panel.getEl().id);
37601 this.fireEvent("paneladded", this, panel);
37606 * Hides the tab for the specified panel.
37607 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37609 hidePanel : function(panel){
37610 if(this.tabs && (panel = this.getPanel(panel))){
37611 this.tabs.hideTab(panel.getEl().id);
37616 * Unhides the tab for a previously hidden panel.
37617 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37619 unhidePanel : function(panel){
37620 if(this.tabs && (panel = this.getPanel(panel))){
37621 this.tabs.unhideTab(panel.getEl().id);
37625 clearPanels : function(){
37626 while(this.panels.getCount() > 0){
37627 this.remove(this.panels.first());
37632 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37633 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37634 * @param {Boolean} preservePanel Overrides the config preservePanel option
37635 * @return {Roo.ContentPanel} The panel that was removed
37637 remove : function(panel, preservePanel)
37639 panel = this.getPanel(panel);
37644 this.fireEvent("beforeremove", this, panel, e);
37645 if(e.cancel === true){
37648 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37649 var panelId = panel.getId();
37650 this.panels.removeKey(panelId);
37652 document.body.appendChild(panel.getEl().dom);
37655 this.tabs.removeTab(panel.getEl().id);
37656 }else if (!preservePanel){
37657 this.bodyEl.dom.removeChild(panel.getEl().dom);
37659 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37660 var p = this.panels.first();
37661 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37662 tempEl.appendChild(p.getEl().dom);
37663 this.bodyEl.update("");
37664 this.bodyEl.dom.appendChild(p.getEl().dom);
37666 this.updateTitle(p.getTitle());
37668 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37669 this.setActivePanel(p);
37671 panel.setRegion(null);
37672 if(this.activePanel == panel){
37673 this.activePanel = null;
37675 if(this.config.autoDestroy !== false && preservePanel !== true){
37676 try{panel.destroy();}catch(e){}
37678 this.fireEvent("panelremoved", this, panel);
37683 * Returns the TabPanel component used by this region
37684 * @return {Roo.TabPanel}
37686 getTabs : function(){
37690 createTool : function(parentEl, className){
37691 var btn = Roo.DomHelper.append(parentEl, {
37693 cls: "x-layout-tools-button",
37696 cls: "roo-layout-tools-button-inner " + className,
37700 btn.addClassOnOver("roo-layout-tools-button-over");
37705 * Ext JS Library 1.1.1
37706 * Copyright(c) 2006-2007, Ext JS, LLC.
37708 * Originally Released Under LGPL - original licence link has changed is not relivant.
37711 * <script type="text/javascript">
37717 * @class Roo.SplitLayoutRegion
37718 * @extends Roo.LayoutRegion
37719 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37721 Roo.bootstrap.layout.Split = function(config){
37722 this.cursor = config.cursor;
37723 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37726 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37728 splitTip : "Drag to resize.",
37729 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37730 useSplitTips : false,
37732 applyConfig : function(config){
37733 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37736 onRender : function(ctr,pos) {
37738 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37739 if(!this.config.split){
37744 var splitEl = Roo.DomHelper.append(ctr.dom, {
37746 id: this.el.id + "-split",
37747 cls: "roo-layout-split roo-layout-split-"+this.position,
37750 /** The SplitBar for this region
37751 * @type Roo.SplitBar */
37752 // does not exist yet...
37753 Roo.log([this.position, this.orientation]);
37755 this.split = new Roo.bootstrap.SplitBar({
37756 dragElement : splitEl,
37757 resizingElement: this.el,
37758 orientation : this.orientation
37761 this.split.on("moved", this.onSplitMove, this);
37762 this.split.useShim = this.config.useShim === true;
37763 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37764 if(this.useSplitTips){
37765 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37767 //if(config.collapsible){
37768 // this.split.el.on("dblclick", this.collapse, this);
37771 if(typeof this.config.minSize != "undefined"){
37772 this.split.minSize = this.config.minSize;
37774 if(typeof this.config.maxSize != "undefined"){
37775 this.split.maxSize = this.config.maxSize;
37777 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37778 this.hideSplitter();
37783 getHMaxSize : function(){
37784 var cmax = this.config.maxSize || 10000;
37785 var center = this.mgr.getRegion("center");
37786 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37789 getVMaxSize : function(){
37790 var cmax = this.config.maxSize || 10000;
37791 var center = this.mgr.getRegion("center");
37792 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37795 onSplitMove : function(split, newSize){
37796 this.fireEvent("resized", this, newSize);
37800 * Returns the {@link Roo.SplitBar} for this region.
37801 * @return {Roo.SplitBar}
37803 getSplitBar : function(){
37808 this.hideSplitter();
37809 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37812 hideSplitter : function(){
37814 this.split.el.setLocation(-2000,-2000);
37815 this.split.el.hide();
37821 this.split.el.show();
37823 Roo.bootstrap.layout.Split.superclass.show.call(this);
37826 beforeSlide: function(){
37827 if(Roo.isGecko){// firefox overflow auto bug workaround
37828 this.bodyEl.clip();
37830 this.tabs.bodyEl.clip();
37832 if(this.activePanel){
37833 this.activePanel.getEl().clip();
37835 if(this.activePanel.beforeSlide){
37836 this.activePanel.beforeSlide();
37842 afterSlide : function(){
37843 if(Roo.isGecko){// firefox overflow auto bug workaround
37844 this.bodyEl.unclip();
37846 this.tabs.bodyEl.unclip();
37848 if(this.activePanel){
37849 this.activePanel.getEl().unclip();
37850 if(this.activePanel.afterSlide){
37851 this.activePanel.afterSlide();
37857 initAutoHide : function(){
37858 if(this.autoHide !== false){
37859 if(!this.autoHideHd){
37860 var st = new Roo.util.DelayedTask(this.slideIn, this);
37861 this.autoHideHd = {
37862 "mouseout": function(e){
37863 if(!e.within(this.el, true)){
37867 "mouseover" : function(e){
37873 this.el.on(this.autoHideHd);
37877 clearAutoHide : function(){
37878 if(this.autoHide !== false){
37879 this.el.un("mouseout", this.autoHideHd.mouseout);
37880 this.el.un("mouseover", this.autoHideHd.mouseover);
37884 clearMonitor : function(){
37885 Roo.get(document).un("click", this.slideInIf, this);
37888 // these names are backwards but not changed for compat
37889 slideOut : function(){
37890 if(this.isSlid || this.el.hasActiveFx()){
37893 this.isSlid = true;
37894 if(this.collapseBtn){
37895 this.collapseBtn.hide();
37897 this.closeBtnState = this.closeBtn.getStyle('display');
37898 this.closeBtn.hide();
37900 this.stickBtn.show();
37903 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37904 this.beforeSlide();
37905 this.el.setStyle("z-index", 10001);
37906 this.el.slideIn(this.getSlideAnchor(), {
37907 callback: function(){
37909 this.initAutoHide();
37910 Roo.get(document).on("click", this.slideInIf, this);
37911 this.fireEvent("slideshow", this);
37918 afterSlideIn : function(){
37919 this.clearAutoHide();
37920 this.isSlid = false;
37921 this.clearMonitor();
37922 this.el.setStyle("z-index", "");
37923 if(this.collapseBtn){
37924 this.collapseBtn.show();
37926 this.closeBtn.setStyle('display', this.closeBtnState);
37928 this.stickBtn.hide();
37930 this.fireEvent("slidehide", this);
37933 slideIn : function(cb){
37934 if(!this.isSlid || this.el.hasActiveFx()){
37938 this.isSlid = false;
37939 this.beforeSlide();
37940 this.el.slideOut(this.getSlideAnchor(), {
37941 callback: function(){
37942 this.el.setLeftTop(-10000, -10000);
37944 this.afterSlideIn();
37952 slideInIf : function(e){
37953 if(!e.within(this.el)){
37958 animateCollapse : function(){
37959 this.beforeSlide();
37960 this.el.setStyle("z-index", 20000);
37961 var anchor = this.getSlideAnchor();
37962 this.el.slideOut(anchor, {
37963 callback : function(){
37964 this.el.setStyle("z-index", "");
37965 this.collapsedEl.slideIn(anchor, {duration:.3});
37967 this.el.setLocation(-10000,-10000);
37969 this.fireEvent("collapsed", this);
37976 animateExpand : function(){
37977 this.beforeSlide();
37978 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37979 this.el.setStyle("z-index", 20000);
37980 this.collapsedEl.hide({
37983 this.el.slideIn(this.getSlideAnchor(), {
37984 callback : function(){
37985 this.el.setStyle("z-index", "");
37988 this.split.el.show();
37990 this.fireEvent("invalidated", this);
37991 this.fireEvent("expanded", this);
38019 getAnchor : function(){
38020 return this.anchors[this.position];
38023 getCollapseAnchor : function(){
38024 return this.canchors[this.position];
38027 getSlideAnchor : function(){
38028 return this.sanchors[this.position];
38031 getAlignAdj : function(){
38032 var cm = this.cmargins;
38033 switch(this.position){
38049 getExpandAdj : function(){
38050 var c = this.collapsedEl, cm = this.cmargins;
38051 switch(this.position){
38053 return [-(cm.right+c.getWidth()+cm.left), 0];
38056 return [cm.right+c.getWidth()+cm.left, 0];
38059 return [0, -(cm.top+cm.bottom+c.getHeight())];
38062 return [0, cm.top+cm.bottom+c.getHeight()];
38068 * Ext JS Library 1.1.1
38069 * Copyright(c) 2006-2007, Ext JS, LLC.
38071 * Originally Released Under LGPL - original licence link has changed is not relivant.
38074 * <script type="text/javascript">
38077 * These classes are private internal classes
38079 Roo.bootstrap.layout.Center = function(config){
38080 config.region = "center";
38081 Roo.bootstrap.layout.Region.call(this, config);
38082 this.visible = true;
38083 this.minWidth = config.minWidth || 20;
38084 this.minHeight = config.minHeight || 20;
38087 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38089 // center panel can't be hidden
38093 // center panel can't be hidden
38096 getMinWidth: function(){
38097 return this.minWidth;
38100 getMinHeight: function(){
38101 return this.minHeight;
38115 Roo.bootstrap.layout.North = function(config)
38117 config.region = 'north';
38118 config.cursor = 'n-resize';
38120 Roo.bootstrap.layout.Split.call(this, config);
38124 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38125 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38126 this.split.el.addClass("roo-layout-split-v");
38128 var size = config.initialSize || config.height;
38129 if(typeof size != "undefined"){
38130 this.el.setHeight(size);
38133 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38135 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38139 getBox : function(){
38140 if(this.collapsed){
38141 return this.collapsedEl.getBox();
38143 var box = this.el.getBox();
38145 box.height += this.split.el.getHeight();
38150 updateBox : function(box){
38151 if(this.split && !this.collapsed){
38152 box.height -= this.split.el.getHeight();
38153 this.split.el.setLeft(box.x);
38154 this.split.el.setTop(box.y+box.height);
38155 this.split.el.setWidth(box.width);
38157 if(this.collapsed){
38158 this.updateBody(box.width, null);
38160 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38168 Roo.bootstrap.layout.South = function(config){
38169 config.region = 'south';
38170 config.cursor = 's-resize';
38171 Roo.bootstrap.layout.Split.call(this, config);
38173 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38174 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38175 this.split.el.addClass("roo-layout-split-v");
38177 var size = config.initialSize || config.height;
38178 if(typeof size != "undefined"){
38179 this.el.setHeight(size);
38183 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38184 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38185 getBox : function(){
38186 if(this.collapsed){
38187 return this.collapsedEl.getBox();
38189 var box = this.el.getBox();
38191 var sh = this.split.el.getHeight();
38198 updateBox : function(box){
38199 if(this.split && !this.collapsed){
38200 var sh = this.split.el.getHeight();
38203 this.split.el.setLeft(box.x);
38204 this.split.el.setTop(box.y-sh);
38205 this.split.el.setWidth(box.width);
38207 if(this.collapsed){
38208 this.updateBody(box.width, null);
38210 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38214 Roo.bootstrap.layout.East = function(config){
38215 config.region = "east";
38216 config.cursor = "e-resize";
38217 Roo.bootstrap.layout.Split.call(this, config);
38219 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38220 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38221 this.split.el.addClass("roo-layout-split-h");
38223 var size = config.initialSize || config.width;
38224 if(typeof size != "undefined"){
38225 this.el.setWidth(size);
38228 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38229 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38230 getBox : function(){
38231 if(this.collapsed){
38232 return this.collapsedEl.getBox();
38234 var box = this.el.getBox();
38236 var sw = this.split.el.getWidth();
38243 updateBox : function(box){
38244 if(this.split && !this.collapsed){
38245 var sw = this.split.el.getWidth();
38247 this.split.el.setLeft(box.x);
38248 this.split.el.setTop(box.y);
38249 this.split.el.setHeight(box.height);
38252 if(this.collapsed){
38253 this.updateBody(null, box.height);
38255 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38259 Roo.bootstrap.layout.West = function(config){
38260 config.region = "west";
38261 config.cursor = "w-resize";
38263 Roo.bootstrap.layout.Split.call(this, config);
38265 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38266 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38267 this.split.el.addClass("roo-layout-split-h");
38271 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38272 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38274 onRender: function(ctr, pos)
38276 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38277 var size = this.config.initialSize || this.config.width;
38278 if(typeof size != "undefined"){
38279 this.el.setWidth(size);
38283 getBox : function(){
38284 if(this.collapsed){
38285 return this.collapsedEl.getBox();
38287 var box = this.el.getBox();
38289 box.width += this.split.el.getWidth();
38294 updateBox : function(box){
38295 if(this.split && !this.collapsed){
38296 var sw = this.split.el.getWidth();
38298 this.split.el.setLeft(box.x+box.width);
38299 this.split.el.setTop(box.y);
38300 this.split.el.setHeight(box.height);
38302 if(this.collapsed){
38303 this.updateBody(null, box.height);
38305 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38307 });Roo.namespace("Roo.bootstrap.panel");/*
38309 * Ext JS Library 1.1.1
38310 * Copyright(c) 2006-2007, Ext JS, LLC.
38312 * Originally Released Under LGPL - original licence link has changed is not relivant.
38315 * <script type="text/javascript">
38318 * @class Roo.ContentPanel
38319 * @extends Roo.util.Observable
38320 * A basic ContentPanel element.
38321 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38322 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38323 * @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
38324 * @cfg {Boolean} closable True if the panel can be closed/removed
38325 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38326 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38327 * @cfg {Toolbar} toolbar A toolbar for this panel
38328 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38329 * @cfg {String} title The title for this panel
38330 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38331 * @cfg {String} url Calls {@link #setUrl} with this value
38332 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38333 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38334 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38335 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38336 * @cfg {Boolean} badges render the badges
38339 * Create a new ContentPanel.
38340 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38341 * @param {String/Object} config A string to set only the title or a config object
38342 * @param {String} content (optional) Set the HTML content for this panel
38343 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38345 Roo.bootstrap.panel.Content = function( config){
38347 this.tpl = config.tpl || false;
38349 var el = config.el;
38350 var content = config.content;
38352 if(config.autoCreate){ // xtype is available if this is called from factory
38355 this.el = Roo.get(el);
38356 if(!this.el && config && config.autoCreate){
38357 if(typeof config.autoCreate == "object"){
38358 if(!config.autoCreate.id){
38359 config.autoCreate.id = config.id||el;
38361 this.el = Roo.DomHelper.append(document.body,
38362 config.autoCreate, true);
38364 var elcfg = { tag: "div",
38365 cls: "roo-layout-inactive-content",
38369 elcfg.html = config.html;
38373 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38376 this.closable = false;
38377 this.loaded = false;
38378 this.active = false;
38381 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38383 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38385 this.wrapEl = this.el; //this.el.wrap();
38387 if (config.toolbar.items) {
38388 ti = config.toolbar.items ;
38389 delete config.toolbar.items ;
38393 this.toolbar.render(this.wrapEl, 'before');
38394 for(var i =0;i < ti.length;i++) {
38395 // Roo.log(['add child', items[i]]);
38396 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38398 this.toolbar.items = nitems;
38399 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38400 delete config.toolbar;
38404 // xtype created footer. - not sure if will work as we normally have to render first..
38405 if (this.footer && !this.footer.el && this.footer.xtype) {
38406 if (!this.wrapEl) {
38407 this.wrapEl = this.el.wrap();
38410 this.footer.container = this.wrapEl.createChild();
38412 this.footer = Roo.factory(this.footer, Roo);
38417 if(typeof config == "string"){
38418 this.title = config;
38420 Roo.apply(this, config);
38424 this.resizeEl = Roo.get(this.resizeEl, true);
38426 this.resizeEl = this.el;
38428 // handle view.xtype
38436 * Fires when this panel is activated.
38437 * @param {Roo.ContentPanel} this
38441 * @event deactivate
38442 * Fires when this panel is activated.
38443 * @param {Roo.ContentPanel} this
38445 "deactivate" : true,
38449 * Fires when this panel is resized if fitToFrame is true.
38450 * @param {Roo.ContentPanel} this
38451 * @param {Number} width The width after any component adjustments
38452 * @param {Number} height The height after any component adjustments
38458 * Fires when this tab is created
38459 * @param {Roo.ContentPanel} this
38470 if(this.autoScroll){
38471 this.resizeEl.setStyle("overflow", "auto");
38473 // fix randome scrolling
38474 //this.el.on('scroll', function() {
38475 // Roo.log('fix random scolling');
38476 // this.scrollTo('top',0);
38479 content = content || this.content;
38481 this.setContent(content);
38483 if(config && config.url){
38484 this.setUrl(this.url, this.params, this.loadOnce);
38489 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38491 if (this.view && typeof(this.view.xtype) != 'undefined') {
38492 this.view.el = this.el.appendChild(document.createElement("div"));
38493 this.view = Roo.factory(this.view);
38494 this.view.render && this.view.render(false, '');
38498 this.fireEvent('render', this);
38501 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38505 setRegion : function(region){
38506 this.region = region;
38507 this.setActiveClass(region && !this.background);
38511 setActiveClass: function(state)
38514 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38515 this.el.setStyle('position','relative');
38517 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38518 this.el.setStyle('position', 'absolute');
38523 * Returns the toolbar for this Panel if one was configured.
38524 * @return {Roo.Toolbar}
38526 getToolbar : function(){
38527 return this.toolbar;
38530 setActiveState : function(active)
38532 this.active = active;
38533 this.setActiveClass(active);
38535 if(this.fireEvent("deactivate", this) === false){
38540 this.fireEvent("activate", this);
38544 * Updates this panel's element
38545 * @param {String} content The new content
38546 * @param {Boolean} loadScripts (optional) true to look for and process scripts
38548 setContent : function(content, loadScripts){
38549 this.el.update(content, loadScripts);
38552 ignoreResize : function(w, h){
38553 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38556 this.lastSize = {width: w, height: h};
38561 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38562 * @return {Roo.UpdateManager} The UpdateManager
38564 getUpdateManager : function(){
38565 return this.el.getUpdateManager();
38568 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38569 * @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:
38572 url: "your-url.php",
38573 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38574 callback: yourFunction,
38575 scope: yourObject, //(optional scope)
38578 text: "Loading...",
38583 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38584 * 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.
38585 * @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}
38586 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38587 * @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.
38588 * @return {Roo.ContentPanel} this
38591 var um = this.el.getUpdateManager();
38592 um.update.apply(um, arguments);
38598 * 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.
38599 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38600 * @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)
38601 * @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)
38602 * @return {Roo.UpdateManager} The UpdateManager
38604 setUrl : function(url, params, loadOnce){
38605 if(this.refreshDelegate){
38606 this.removeListener("activate", this.refreshDelegate);
38608 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38609 this.on("activate", this.refreshDelegate);
38610 return this.el.getUpdateManager();
38613 _handleRefresh : function(url, params, loadOnce){
38614 if(!loadOnce || !this.loaded){
38615 var updater = this.el.getUpdateManager();
38616 updater.update(url, params, this._setLoaded.createDelegate(this));
38620 _setLoaded : function(){
38621 this.loaded = true;
38625 * Returns this panel's id
38628 getId : function(){
38633 * Returns this panel's element - used by regiosn to add.
38634 * @return {Roo.Element}
38636 getEl : function(){
38637 return this.wrapEl || this.el;
38642 adjustForComponents : function(width, height)
38644 //Roo.log('adjustForComponents ');
38645 if(this.resizeEl != this.el){
38646 width -= this.el.getFrameWidth('lr');
38647 height -= this.el.getFrameWidth('tb');
38650 var te = this.toolbar.getEl();
38651 te.setWidth(width);
38652 height -= te.getHeight();
38655 var te = this.footer.getEl();
38656 te.setWidth(width);
38657 height -= te.getHeight();
38661 if(this.adjustments){
38662 width += this.adjustments[0];
38663 height += this.adjustments[1];
38665 return {"width": width, "height": height};
38668 setSize : function(width, height){
38669 if(this.fitToFrame && !this.ignoreResize(width, height)){
38670 if(this.fitContainer && this.resizeEl != this.el){
38671 this.el.setSize(width, height);
38673 var size = this.adjustForComponents(width, height);
38674 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38675 this.fireEvent('resize', this, size.width, size.height);
38680 * Returns this panel's title
38683 getTitle : function(){
38685 if (typeof(this.title) != 'object') {
38690 for (var k in this.title) {
38691 if (!this.title.hasOwnProperty(k)) {
38695 if (k.indexOf('-') >= 0) {
38696 var s = k.split('-');
38697 for (var i = 0; i<s.length; i++) {
38698 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38701 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38708 * Set this panel's title
38709 * @param {String} title
38711 setTitle : function(title){
38712 this.title = title;
38714 this.region.updatePanelTitle(this, title);
38719 * Returns true is this panel was configured to be closable
38720 * @return {Boolean}
38722 isClosable : function(){
38723 return this.closable;
38726 beforeSlide : function(){
38728 this.resizeEl.clip();
38731 afterSlide : function(){
38733 this.resizeEl.unclip();
38737 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38738 * Will fail silently if the {@link #setUrl} method has not been called.
38739 * This does not activate the panel, just updates its content.
38741 refresh : function(){
38742 if(this.refreshDelegate){
38743 this.loaded = false;
38744 this.refreshDelegate();
38749 * Destroys this panel
38751 destroy : function(){
38752 this.el.removeAllListeners();
38753 var tempEl = document.createElement("span");
38754 tempEl.appendChild(this.el.dom);
38755 tempEl.innerHTML = "";
38761 * form - if the content panel contains a form - this is a reference to it.
38762 * @type {Roo.form.Form}
38766 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38767 * This contains a reference to it.
38773 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38783 * @param {Object} cfg Xtype definition of item to add.
38787 getChildContainer: function () {
38788 return this.getEl();
38793 var ret = new Roo.factory(cfg);
38798 if (cfg.xtype.match(/^Form$/)) {
38801 //if (this.footer) {
38802 // el = this.footer.container.insertSibling(false, 'before');
38804 el = this.el.createChild();
38807 this.form = new Roo.form.Form(cfg);
38810 if ( this.form.allItems.length) {
38811 this.form.render(el.dom);
38815 // should only have one of theses..
38816 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38817 // views.. should not be just added - used named prop 'view''
38819 cfg.el = this.el.appendChild(document.createElement("div"));
38822 var ret = new Roo.factory(cfg);
38824 ret.render && ret.render(false, ''); // render blank..
38834 * @class Roo.bootstrap.panel.Grid
38835 * @extends Roo.bootstrap.panel.Content
38837 * Create a new GridPanel.
38838 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38839 * @param {Object} config A the config object
38845 Roo.bootstrap.panel.Grid = function(config)
38849 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38850 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38852 config.el = this.wrapper;
38853 //this.el = this.wrapper;
38855 if (config.container) {
38856 // ctor'ed from a Border/panel.grid
38859 this.wrapper.setStyle("overflow", "hidden");
38860 this.wrapper.addClass('roo-grid-container');
38865 if(config.toolbar){
38866 var tool_el = this.wrapper.createChild();
38867 this.toolbar = Roo.factory(config.toolbar);
38869 if (config.toolbar.items) {
38870 ti = config.toolbar.items ;
38871 delete config.toolbar.items ;
38875 this.toolbar.render(tool_el);
38876 for(var i =0;i < ti.length;i++) {
38877 // Roo.log(['add child', items[i]]);
38878 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38880 this.toolbar.items = nitems;
38882 delete config.toolbar;
38885 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38886 config.grid.scrollBody = true;;
38887 config.grid.monitorWindowResize = false; // turn off autosizing
38888 config.grid.autoHeight = false;
38889 config.grid.autoWidth = false;
38891 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38893 if (config.background) {
38894 // render grid on panel activation (if panel background)
38895 this.on('activate', function(gp) {
38896 if (!gp.grid.rendered) {
38897 gp.grid.render(this.wrapper);
38898 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38903 this.grid.render(this.wrapper);
38904 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38907 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38908 // ??? needed ??? config.el = this.wrapper;
38913 // xtype created footer. - not sure if will work as we normally have to render first..
38914 if (this.footer && !this.footer.el && this.footer.xtype) {
38916 var ctr = this.grid.getView().getFooterPanel(true);
38917 this.footer.dataSource = this.grid.dataSource;
38918 this.footer = Roo.factory(this.footer, Roo);
38919 this.footer.render(ctr);
38929 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38930 getId : function(){
38931 return this.grid.id;
38935 * Returns the grid for this panel
38936 * @return {Roo.bootstrap.Table}
38938 getGrid : function(){
38942 setSize : function(width, height){
38943 if(!this.ignoreResize(width, height)){
38944 var grid = this.grid;
38945 var size = this.adjustForComponents(width, height);
38946 var gridel = grid.getGridEl();
38947 gridel.setSize(size.width, size.height);
38949 var thd = grid.getGridEl().select('thead',true).first();
38950 var tbd = grid.getGridEl().select('tbody', true).first();
38952 tbd.setSize(width, height - thd.getHeight());
38961 beforeSlide : function(){
38962 this.grid.getView().scroller.clip();
38965 afterSlide : function(){
38966 this.grid.getView().scroller.unclip();
38969 destroy : function(){
38970 this.grid.destroy();
38972 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38977 * @class Roo.bootstrap.panel.Nest
38978 * @extends Roo.bootstrap.panel.Content
38980 * Create a new Panel, that can contain a layout.Border.
38983 * @param {Roo.BorderLayout} layout The layout for this panel
38984 * @param {String/Object} config A string to set only the title or a config object
38986 Roo.bootstrap.panel.Nest = function(config)
38988 // construct with only one argument..
38989 /* FIXME - implement nicer consturctors
38990 if (layout.layout) {
38992 layout = config.layout;
38993 delete config.layout;
38995 if (layout.xtype && !layout.getEl) {
38996 // then layout needs constructing..
38997 layout = Roo.factory(layout, Roo);
39001 config.el = config.layout.getEl();
39003 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39005 config.layout.monitorWindowResize = false; // turn off autosizing
39006 this.layout = config.layout;
39007 this.layout.getEl().addClass("roo-layout-nested-layout");
39008 this.layout.parent = this;
39015 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39017 setSize : function(width, height){
39018 if(!this.ignoreResize(width, height)){
39019 var size = this.adjustForComponents(width, height);
39020 var el = this.layout.getEl();
39021 if (size.height < 1) {
39022 el.setWidth(size.width);
39024 el.setSize(size.width, size.height);
39026 var touch = el.dom.offsetWidth;
39027 this.layout.layout();
39028 // ie requires a double layout on the first pass
39029 if(Roo.isIE && !this.initialized){
39030 this.initialized = true;
39031 this.layout.layout();
39036 // activate all subpanels if not currently active..
39038 setActiveState : function(active){
39039 this.active = active;
39040 this.setActiveClass(active);
39043 this.fireEvent("deactivate", this);
39047 this.fireEvent("activate", this);
39048 // not sure if this should happen before or after..
39049 if (!this.layout) {
39050 return; // should not happen..
39053 for (var r in this.layout.regions) {
39054 reg = this.layout.getRegion(r);
39055 if (reg.getActivePanel()) {
39056 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39057 reg.setActivePanel(reg.getActivePanel());
39060 if (!reg.panels.length) {
39063 reg.showPanel(reg.getPanel(0));
39072 * Returns the nested BorderLayout for this panel
39073 * @return {Roo.BorderLayout}
39075 getLayout : function(){
39076 return this.layout;
39080 * Adds a xtype elements to the layout of the nested panel
39084 xtype : 'ContentPanel',
39091 xtype : 'NestedLayoutPanel',
39097 items : [ ... list of content panels or nested layout panels.. ]
39101 * @param {Object} cfg Xtype definition of item to add.
39103 addxtype : function(cfg) {
39104 return this.layout.addxtype(cfg);
39109 * Ext JS Library 1.1.1
39110 * Copyright(c) 2006-2007, Ext JS, LLC.
39112 * Originally Released Under LGPL - original licence link has changed is not relivant.
39115 * <script type="text/javascript">
39118 * @class Roo.TabPanel
39119 * @extends Roo.util.Observable
39120 * A lightweight tab container.
39124 // basic tabs 1, built from existing content
39125 var tabs = new Roo.TabPanel("tabs1");
39126 tabs.addTab("script", "View Script");
39127 tabs.addTab("markup", "View Markup");
39128 tabs.activate("script");
39130 // more advanced tabs, built from javascript
39131 var jtabs = new Roo.TabPanel("jtabs");
39132 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39134 // set up the UpdateManager
39135 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39136 var updater = tab2.getUpdateManager();
39137 updater.setDefaultUrl("ajax1.htm");
39138 tab2.on('activate', updater.refresh, updater, true);
39140 // Use setUrl for Ajax loading
39141 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39142 tab3.setUrl("ajax2.htm", null, true);
39145 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39148 jtabs.activate("jtabs-1");
39151 * Create a new TabPanel.
39152 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39153 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39155 Roo.bootstrap.panel.Tabs = function(config){
39157 * The container element for this TabPanel.
39158 * @type Roo.Element
39160 this.el = Roo.get(config.el);
39163 if(typeof config == "boolean"){
39164 this.tabPosition = config ? "bottom" : "top";
39166 Roo.apply(this, config);
39170 if(this.tabPosition == "bottom"){
39171 // if tabs are at the bottom = create the body first.
39172 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39173 this.el.addClass("roo-tabs-bottom");
39175 // next create the tabs holders
39177 if (this.tabPosition == "west"){
39179 var reg = this.region; // fake it..
39181 if (!reg.mgr.parent) {
39184 reg = reg.mgr.parent.region;
39186 Roo.log("got nest?");
39188 if (reg.mgr.getRegion('west')) {
39189 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39190 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39191 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39192 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39193 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39201 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39202 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39203 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39204 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39209 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39212 // finally - if tabs are at the top, then create the body last..
39213 if(this.tabPosition != "bottom"){
39214 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39215 * @type Roo.Element
39217 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39218 this.el.addClass("roo-tabs-top");
39222 this.bodyEl.setStyle("position", "relative");
39224 this.active = null;
39225 this.activateDelegate = this.activate.createDelegate(this);
39230 * Fires when the active tab changes
39231 * @param {Roo.TabPanel} this
39232 * @param {Roo.TabPanelItem} activePanel The new active tab
39236 * @event beforetabchange
39237 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39238 * @param {Roo.TabPanel} this
39239 * @param {Object} e Set cancel to true on this object to cancel the tab change
39240 * @param {Roo.TabPanelItem} tab The tab being changed to
39242 "beforetabchange" : true
39245 Roo.EventManager.onWindowResize(this.onResize, this);
39246 this.cpad = this.el.getPadding("lr");
39247 this.hiddenCount = 0;
39250 // toolbar on the tabbar support...
39251 if (this.toolbar) {
39252 alert("no toolbar support yet");
39253 this.toolbar = false;
39255 var tcfg = this.toolbar;
39256 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39257 this.toolbar = new Roo.Toolbar(tcfg);
39258 if (Roo.isSafari) {
39259 var tbl = tcfg.container.child('table', true);
39260 tbl.setAttribute('width', '100%');
39268 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39271 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39273 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39275 tabPosition : "top",
39277 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39279 currentTabWidth : 0,
39281 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39285 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39289 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39291 preferredTabWidth : 175,
39293 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39295 resizeTabs : false,
39297 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39299 monitorResize : true,
39301 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39303 toolbar : false, // set by caller..
39305 region : false, /// set by caller
39307 disableTooltips : true, // not used yet...
39310 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39311 * @param {String} id The id of the div to use <b>or create</b>
39312 * @param {String} text The text for the tab
39313 * @param {String} content (optional) Content to put in the TabPanelItem body
39314 * @param {Boolean} closable (optional) True to create a close icon on the tab
39315 * @return {Roo.TabPanelItem} The created TabPanelItem
39317 addTab : function(id, text, content, closable, tpl)
39319 var item = new Roo.bootstrap.panel.TabItem({
39323 closable : closable,
39326 this.addTabItem(item);
39328 item.setContent(content);
39334 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39335 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39336 * @return {Roo.TabPanelItem}
39338 getTab : function(id){
39339 return this.items[id];
39343 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39344 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39346 hideTab : function(id){
39347 var t = this.items[id];
39350 this.hiddenCount++;
39351 this.autoSizeTabs();
39356 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39357 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39359 unhideTab : function(id){
39360 var t = this.items[id];
39362 t.setHidden(false);
39363 this.hiddenCount--;
39364 this.autoSizeTabs();
39369 * Adds an existing {@link Roo.TabPanelItem}.
39370 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39372 addTabItem : function(item)
39374 this.items[item.id] = item;
39375 this.items.push(item);
39376 this.autoSizeTabs();
39377 // if(this.resizeTabs){
39378 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39379 // this.autoSizeTabs();
39381 // item.autoSize();
39386 * Removes a {@link Roo.TabPanelItem}.
39387 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39389 removeTab : function(id){
39390 var items = this.items;
39391 var tab = items[id];
39392 if(!tab) { return; }
39393 var index = items.indexOf(tab);
39394 if(this.active == tab && items.length > 1){
39395 var newTab = this.getNextAvailable(index);
39400 this.stripEl.dom.removeChild(tab.pnode.dom);
39401 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39402 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39404 items.splice(index, 1);
39405 delete this.items[tab.id];
39406 tab.fireEvent("close", tab);
39407 tab.purgeListeners();
39408 this.autoSizeTabs();
39411 getNextAvailable : function(start){
39412 var items = this.items;
39414 // look for a next tab that will slide over to
39415 // replace the one being removed
39416 while(index < items.length){
39417 var item = items[++index];
39418 if(item && !item.isHidden()){
39422 // if one isn't found select the previous tab (on the left)
39425 var item = items[--index];
39426 if(item && !item.isHidden()){
39434 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39435 * @param {String/Number} id The id or index of the TabPanelItem to disable.
39437 disableTab : function(id){
39438 var tab = this.items[id];
39439 if(tab && this.active != tab){
39445 * Enables a {@link Roo.TabPanelItem} that is disabled.
39446 * @param {String/Number} id The id or index of the TabPanelItem to enable.
39448 enableTab : function(id){
39449 var tab = this.items[id];
39454 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39455 * @param {String/Number} id The id or index of the TabPanelItem to activate.
39456 * @return {Roo.TabPanelItem} The TabPanelItem.
39458 activate : function(id)
39460 //Roo.log('activite:' + id);
39462 var tab = this.items[id];
39466 if(tab == this.active || tab.disabled){
39470 this.fireEvent("beforetabchange", this, e, tab);
39471 if(e.cancel !== true && !tab.disabled){
39473 this.active.hide();
39475 this.active = this.items[id];
39476 this.active.show();
39477 this.fireEvent("tabchange", this, this.active);
39483 * Gets the active {@link Roo.TabPanelItem}.
39484 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39486 getActiveTab : function(){
39487 return this.active;
39491 * Updates the tab body element to fit the height of the container element
39492 * for overflow scrolling
39493 * @param {Number} targetHeight (optional) Override the starting height from the elements height
39495 syncHeight : function(targetHeight){
39496 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39497 var bm = this.bodyEl.getMargins();
39498 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39499 this.bodyEl.setHeight(newHeight);
39503 onResize : function(){
39504 if(this.monitorResize){
39505 this.autoSizeTabs();
39510 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39512 beginUpdate : function(){
39513 this.updating = true;
39517 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39519 endUpdate : function(){
39520 this.updating = false;
39521 this.autoSizeTabs();
39525 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39527 autoSizeTabs : function()
39529 var count = this.items.length;
39530 var vcount = count - this.hiddenCount;
39533 this.stripEl.hide();
39535 this.stripEl.show();
39538 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39543 var w = Math.max(this.el.getWidth() - this.cpad, 10);
39544 var availWidth = Math.floor(w / vcount);
39545 var b = this.stripBody;
39546 if(b.getWidth() > w){
39547 var tabs = this.items;
39548 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39549 if(availWidth < this.minTabWidth){
39550 /*if(!this.sleft){ // incomplete scrolling code
39551 this.createScrollButtons();
39554 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39557 if(this.currentTabWidth < this.preferredTabWidth){
39558 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39564 * Returns the number of tabs in this TabPanel.
39567 getCount : function(){
39568 return this.items.length;
39572 * Resizes all the tabs to the passed width
39573 * @param {Number} The new width
39575 setTabWidth : function(width){
39576 this.currentTabWidth = width;
39577 for(var i = 0, len = this.items.length; i < len; i++) {
39578 if(!this.items[i].isHidden()) {
39579 this.items[i].setWidth(width);
39585 * Destroys this TabPanel
39586 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39588 destroy : function(removeEl){
39589 Roo.EventManager.removeResizeListener(this.onResize, this);
39590 for(var i = 0, len = this.items.length; i < len; i++){
39591 this.items[i].purgeListeners();
39593 if(removeEl === true){
39594 this.el.update("");
39599 createStrip : function(container)
39601 var strip = document.createElement("nav");
39602 strip.className = Roo.bootstrap.version == 4 ?
39603 "navbar-light bg-light" :
39604 "navbar navbar-default"; //"x-tabs-wrap";
39605 container.appendChild(strip);
39609 createStripList : function(strip)
39611 // div wrapper for retard IE
39612 // returns the "tr" element.
39613 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39614 //'<div class="x-tabs-strip-wrap">'+
39615 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39616 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39617 return strip.firstChild; //.firstChild.firstChild.firstChild;
39619 createBody : function(container)
39621 var body = document.createElement("div");
39622 Roo.id(body, "tab-body");
39623 //Roo.fly(body).addClass("x-tabs-body");
39624 Roo.fly(body).addClass("tab-content");
39625 container.appendChild(body);
39628 createItemBody :function(bodyEl, id){
39629 var body = Roo.getDom(id);
39631 body = document.createElement("div");
39634 //Roo.fly(body).addClass("x-tabs-item-body");
39635 Roo.fly(body).addClass("tab-pane");
39636 bodyEl.insertBefore(body, bodyEl.firstChild);
39640 createStripElements : function(stripEl, text, closable, tpl)
39642 var td = document.createElement("li"); // was td..
39643 td.className = 'nav-item';
39645 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39648 stripEl.appendChild(td);
39650 td.className = "x-tabs-closable";
39651 if(!this.closeTpl){
39652 this.closeTpl = new Roo.Template(
39653 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39654 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39655 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
39658 var el = this.closeTpl.overwrite(td, {"text": text});
39659 var close = el.getElementsByTagName("div")[0];
39660 var inner = el.getElementsByTagName("em")[0];
39661 return {"el": el, "close": close, "inner": inner};
39664 // not sure what this is..
39665 // if(!this.tabTpl){
39666 //this.tabTpl = new Roo.Template(
39667 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39668 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39670 // this.tabTpl = new Roo.Template(
39671 // '<a href="#">' +
39672 // '<span unselectable="on"' +
39673 // (this.disableTooltips ? '' : ' title="{text}"') +
39674 // ' >{text}</span></a>'
39680 var template = tpl || this.tabTpl || false;
39683 template = new Roo.Template(
39684 Roo.bootstrap.version == 4 ?
39686 '<a class="nav-link" href="#" unselectable="on"' +
39687 (this.disableTooltips ? '' : ' title="{text}"') +
39690 '<a class="nav-link" href="#">' +
39691 '<span unselectable="on"' +
39692 (this.disableTooltips ? '' : ' title="{text}"') +
39693 ' >{text}</span></a>'
39698 switch (typeof(template)) {
39702 template = new Roo.Template(template);
39708 var el = template.overwrite(td, {"text": text});
39710 var inner = el.getElementsByTagName("span")[0];
39712 return {"el": el, "inner": inner};
39720 * @class Roo.TabPanelItem
39721 * @extends Roo.util.Observable
39722 * Represents an individual item (tab plus body) in a TabPanel.
39723 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39724 * @param {String} id The id of this TabPanelItem
39725 * @param {String} text The text for the tab of this TabPanelItem
39726 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39728 Roo.bootstrap.panel.TabItem = function(config){
39730 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39731 * @type Roo.TabPanel
39733 this.tabPanel = config.panel;
39735 * The id for this TabPanelItem
39738 this.id = config.id;
39740 this.disabled = false;
39742 this.text = config.text;
39744 this.loaded = false;
39745 this.closable = config.closable;
39748 * The body element for this TabPanelItem.
39749 * @type Roo.Element
39751 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39752 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39753 this.bodyEl.setStyle("display", "block");
39754 this.bodyEl.setStyle("zoom", "1");
39755 //this.hideAction();
39757 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39759 this.el = Roo.get(els.el);
39760 this.inner = Roo.get(els.inner, true);
39761 this.textEl = Roo.bootstrap.version == 4 ?
39762 this.el : Roo.get(this.el.dom.firstChild, true);
39764 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39765 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39768 // this.el.on("mousedown", this.onTabMouseDown, this);
39769 this.el.on("click", this.onTabClick, this);
39771 if(config.closable){
39772 var c = Roo.get(els.close, true);
39773 c.dom.title = this.closeText;
39774 c.addClassOnOver("close-over");
39775 c.on("click", this.closeClick, this);
39781 * Fires when this tab becomes the active tab.
39782 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39783 * @param {Roo.TabPanelItem} this
39787 * @event beforeclose
39788 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39789 * @param {Roo.TabPanelItem} this
39790 * @param {Object} e Set cancel to true on this object to cancel the close.
39792 "beforeclose": true,
39795 * Fires when this tab is closed.
39796 * @param {Roo.TabPanelItem} this
39800 * @event deactivate
39801 * Fires when this tab is no longer the active tab.
39802 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39803 * @param {Roo.TabPanelItem} this
39805 "deactivate" : true
39807 this.hidden = false;
39809 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39812 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39814 purgeListeners : function(){
39815 Roo.util.Observable.prototype.purgeListeners.call(this);
39816 this.el.removeAllListeners();
39819 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39822 this.status_node.addClass("active");
39825 this.tabPanel.stripWrap.repaint();
39827 this.fireEvent("activate", this.tabPanel, this);
39831 * Returns true if this tab is the active tab.
39832 * @return {Boolean}
39834 isActive : function(){
39835 return this.tabPanel.getActiveTab() == this;
39839 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39842 this.status_node.removeClass("active");
39844 this.fireEvent("deactivate", this.tabPanel, this);
39847 hideAction : function(){
39848 this.bodyEl.hide();
39849 this.bodyEl.setStyle("position", "absolute");
39850 this.bodyEl.setLeft("-20000px");
39851 this.bodyEl.setTop("-20000px");
39854 showAction : function(){
39855 this.bodyEl.setStyle("position", "relative");
39856 this.bodyEl.setTop("");
39857 this.bodyEl.setLeft("");
39858 this.bodyEl.show();
39862 * Set the tooltip for the tab.
39863 * @param {String} tooltip The tab's tooltip
39865 setTooltip : function(text){
39866 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39867 this.textEl.dom.qtip = text;
39868 this.textEl.dom.removeAttribute('title');
39870 this.textEl.dom.title = text;
39874 onTabClick : function(e){
39875 e.preventDefault();
39876 this.tabPanel.activate(this.id);
39879 onTabMouseDown : function(e){
39880 e.preventDefault();
39881 this.tabPanel.activate(this.id);
39884 getWidth : function(){
39885 return this.inner.getWidth();
39888 setWidth : function(width){
39889 var iwidth = width - this.linode.getPadding("lr");
39890 this.inner.setWidth(iwidth);
39891 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39892 this.linode.setWidth(width);
39896 * Show or hide the tab
39897 * @param {Boolean} hidden True to hide or false to show.
39899 setHidden : function(hidden){
39900 this.hidden = hidden;
39901 this.linode.setStyle("display", hidden ? "none" : "");
39905 * Returns true if this tab is "hidden"
39906 * @return {Boolean}
39908 isHidden : function(){
39909 return this.hidden;
39913 * Returns the text for this tab
39916 getText : function(){
39920 autoSize : function(){
39921 //this.el.beginMeasure();
39922 this.textEl.setWidth(1);
39924 * #2804 [new] Tabs in Roojs
39925 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39927 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39928 //this.el.endMeasure();
39932 * Sets the text for the tab (Note: this also sets the tooltip text)
39933 * @param {String} text The tab's text and tooltip
39935 setText : function(text){
39937 this.textEl.update(text);
39938 this.setTooltip(text);
39939 //if(!this.tabPanel.resizeTabs){
39940 // this.autoSize();
39944 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39946 activate : function(){
39947 this.tabPanel.activate(this.id);
39951 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39953 disable : function(){
39954 if(this.tabPanel.active != this){
39955 this.disabled = true;
39956 this.status_node.addClass("disabled");
39961 * Enables this TabPanelItem if it was previously disabled.
39963 enable : function(){
39964 this.disabled = false;
39965 this.status_node.removeClass("disabled");
39969 * Sets the content for this TabPanelItem.
39970 * @param {String} content The content
39971 * @param {Boolean} loadScripts true to look for and load scripts
39973 setContent : function(content, loadScripts){
39974 this.bodyEl.update(content, loadScripts);
39978 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39979 * @return {Roo.UpdateManager} The UpdateManager
39981 getUpdateManager : function(){
39982 return this.bodyEl.getUpdateManager();
39986 * Set a URL to be used to load the content for this TabPanelItem.
39987 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39988 * @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)
39989 * @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)
39990 * @return {Roo.UpdateManager} The UpdateManager
39992 setUrl : function(url, params, loadOnce){
39993 if(this.refreshDelegate){
39994 this.un('activate', this.refreshDelegate);
39996 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39997 this.on("activate", this.refreshDelegate);
39998 return this.bodyEl.getUpdateManager();
40002 _handleRefresh : function(url, params, loadOnce){
40003 if(!loadOnce || !this.loaded){
40004 var updater = this.bodyEl.getUpdateManager();
40005 updater.update(url, params, this._setLoaded.createDelegate(this));
40010 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40011 * Will fail silently if the setUrl method has not been called.
40012 * This does not activate the panel, just updates its content.
40014 refresh : function(){
40015 if(this.refreshDelegate){
40016 this.loaded = false;
40017 this.refreshDelegate();
40022 _setLoaded : function(){
40023 this.loaded = true;
40027 closeClick : function(e){
40030 this.fireEvent("beforeclose", this, o);
40031 if(o.cancel !== true){
40032 this.tabPanel.removeTab(this.id);
40036 * The text displayed in the tooltip for the close icon.
40039 closeText : "Close this tab"
40042 * This script refer to:
40043 * Title: International Telephone Input
40044 * Author: Jack O'Connor
40045 * Code version: v12.1.12
40046 * Availability: https://github.com/jackocnr/intl-tel-input.git
40049 Roo.bootstrap.PhoneInputData = function() {
40052 "Afghanistan (افغانستان)",
40057 "Albania (Shqipëri)",
40062 "Algeria (الجزائر)",
40087 "Antigua and Barbuda",
40097 "Armenia (Հայաստան)",
40113 "Austria (Österreich)",
40118 "Azerbaijan (Azərbaycan)",
40128 "Bahrain (البحرين)",
40133 "Bangladesh (বাংলাদেশ)",
40143 "Belarus (Беларусь)",
40148 "Belgium (België)",
40178 "Bosnia and Herzegovina (Босна и Херцеговина)",
40193 "British Indian Ocean Territory",
40198 "British Virgin Islands",
40208 "Bulgaria (България)",
40218 "Burundi (Uburundi)",
40223 "Cambodia (កម្ពុជា)",
40228 "Cameroon (Cameroun)",
40237 ["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"]
40240 "Cape Verde (Kabu Verdi)",
40245 "Caribbean Netherlands",
40256 "Central African Republic (République centrafricaine)",
40276 "Christmas Island",
40282 "Cocos (Keeling) Islands",
40293 "Comoros (جزر القمر)",
40298 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40303 "Congo (Republic) (Congo-Brazzaville)",
40323 "Croatia (Hrvatska)",
40344 "Czech Republic (Česká republika)",
40349 "Denmark (Danmark)",
40364 "Dominican Republic (República Dominicana)",
40368 ["809", "829", "849"]
40386 "Equatorial Guinea (Guinea Ecuatorial)",
40406 "Falkland Islands (Islas Malvinas)",
40411 "Faroe Islands (Føroyar)",
40432 "French Guiana (Guyane française)",
40437 "French Polynesia (Polynésie française)",
40452 "Georgia (საქართველო)",
40457 "Germany (Deutschland)",
40477 "Greenland (Kalaallit Nunaat)",
40514 "Guinea-Bissau (Guiné Bissau)",
40539 "Hungary (Magyarország)",
40544 "Iceland (Ísland)",
40564 "Iraq (العراق)",
40580 "Israel (ישראל)",
40607 "Jordan (الأردن)",
40612 "Kazakhstan (Казахстан)",
40633 "Kuwait (الكويت)",
40638 "Kyrgyzstan (Кыргызстан)",
40648 "Latvia (Latvija)",
40653 "Lebanon (لبنان)",
40668 "Libya (ليبيا)",
40678 "Lithuania (Lietuva)",
40693 "Macedonia (FYROM) (Македонија)",
40698 "Madagascar (Madagasikara)",
40728 "Marshall Islands",
40738 "Mauritania (موريتانيا)",
40743 "Mauritius (Moris)",
40764 "Moldova (Republica Moldova)",
40774 "Mongolia (Монгол)",
40779 "Montenegro (Crna Gora)",
40789 "Morocco (المغرب)",
40795 "Mozambique (Moçambique)",
40800 "Myanmar (Burma) (မြန်မာ)",
40805 "Namibia (Namibië)",
40820 "Netherlands (Nederland)",
40825 "New Caledonia (Nouvelle-Calédonie)",
40860 "North Korea (조선 민주주의 인민 공화국)",
40865 "Northern Mariana Islands",
40881 "Pakistan (پاکستان)",
40891 "Palestine (فلسطين)",
40901 "Papua New Guinea",
40943 "Réunion (La Réunion)",
40949 "Romania (România)",
40965 "Saint Barthélemy",
40976 "Saint Kitts and Nevis",
40986 "Saint Martin (Saint-Martin (partie française))",
40992 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40997 "Saint Vincent and the Grenadines",
41012 "São Tomé and Príncipe (São Tomé e Príncipe)",
41017 "Saudi Arabia (المملكة العربية السعودية)",
41022 "Senegal (Sénégal)",
41052 "Slovakia (Slovensko)",
41057 "Slovenia (Slovenija)",
41067 "Somalia (Soomaaliya)",
41077 "South Korea (대한민국)",
41082 "South Sudan (جنوب السودان)",
41092 "Sri Lanka (ශ්රී ලංකාව)",
41097 "Sudan (السودان)",
41107 "Svalbard and Jan Mayen",
41118 "Sweden (Sverige)",
41123 "Switzerland (Schweiz)",
41128 "Syria (سوريا)",
41173 "Trinidad and Tobago",
41178 "Tunisia (تونس)",
41183 "Turkey (Türkiye)",
41193 "Turks and Caicos Islands",
41203 "U.S. Virgin Islands",
41213 "Ukraine (Україна)",
41218 "United Arab Emirates (الإمارات العربية المتحدة)",
41240 "Uzbekistan (Oʻzbekiston)",
41250 "Vatican City (Città del Vaticano)",
41261 "Vietnam (Việt Nam)",
41266 "Wallis and Futuna (Wallis-et-Futuna)",
41271 "Western Sahara (الصحراء الغربية)",
41277 "Yemen (اليمن)",
41301 * This script refer to:
41302 * Title: International Telephone Input
41303 * Author: Jack O'Connor
41304 * Code version: v12.1.12
41305 * Availability: https://github.com/jackocnr/intl-tel-input.git
41309 * @class Roo.bootstrap.PhoneInput
41310 * @extends Roo.bootstrap.TriggerField
41311 * An input with International dial-code selection
41313 * @cfg {String} defaultDialCode default '+852'
41314 * @cfg {Array} preferedCountries default []
41317 * Create a new PhoneInput.
41318 * @param {Object} config Configuration options
41321 Roo.bootstrap.PhoneInput = function(config) {
41322 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41325 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41327 listWidth: undefined,
41329 selectedClass: 'active',
41331 invalidClass : "has-warning",
41333 validClass: 'has-success',
41335 allowed: '0123456789',
41340 * @cfg {String} defaultDialCode The default dial code when initializing the input
41342 defaultDialCode: '+852',
41345 * @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
41347 preferedCountries: false,
41349 getAutoCreate : function()
41351 var data = Roo.bootstrap.PhoneInputData();
41352 var align = this.labelAlign || this.parentLabelAlign();
41355 this.allCountries = [];
41356 this.dialCodeMapping = [];
41358 for (var i = 0; i < data.length; i++) {
41360 this.allCountries[i] = {
41364 priority: c[3] || 0,
41365 areaCodes: c[4] || null
41367 this.dialCodeMapping[c[2]] = {
41370 priority: c[3] || 0,
41371 areaCodes: c[4] || null
41383 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41384 maxlength: this.max_length,
41385 cls : 'form-control tel-input',
41386 autocomplete: 'new-password'
41389 var hiddenInput = {
41392 cls: 'hidden-tel-input'
41396 hiddenInput.name = this.name;
41399 if (this.disabled) {
41400 input.disabled = true;
41403 var flag_container = {
41420 cls: this.hasFeedback ? 'has-feedback' : '',
41426 cls: 'dial-code-holder',
41433 cls: 'roo-select2-container input-group',
41440 if (this.fieldLabel.length) {
41443 tooltip: 'This field is required'
41449 cls: 'control-label',
41455 html: this.fieldLabel
41458 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41464 if(this.indicatorpos == 'right') {
41465 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41472 if(align == 'left') {
41480 if(this.labelWidth > 12){
41481 label.style = "width: " + this.labelWidth + 'px';
41483 if(this.labelWidth < 13 && this.labelmd == 0){
41484 this.labelmd = this.labelWidth;
41486 if(this.labellg > 0){
41487 label.cls += ' col-lg-' + this.labellg;
41488 input.cls += ' col-lg-' + (12 - this.labellg);
41490 if(this.labelmd > 0){
41491 label.cls += ' col-md-' + this.labelmd;
41492 container.cls += ' col-md-' + (12 - this.labelmd);
41494 if(this.labelsm > 0){
41495 label.cls += ' col-sm-' + this.labelsm;
41496 container.cls += ' col-sm-' + (12 - this.labelsm);
41498 if(this.labelxs > 0){
41499 label.cls += ' col-xs-' + this.labelxs;
41500 container.cls += ' col-xs-' + (12 - this.labelxs);
41510 var settings = this;
41512 ['xs','sm','md','lg'].map(function(size){
41513 if (settings[size]) {
41514 cfg.cls += ' col-' + size + '-' + settings[size];
41518 this.store = new Roo.data.Store({
41519 proxy : new Roo.data.MemoryProxy({}),
41520 reader : new Roo.data.JsonReader({
41531 'name' : 'dialCode',
41535 'name' : 'priority',
41539 'name' : 'areaCodes',
41546 if(!this.preferedCountries) {
41547 this.preferedCountries = [
41554 var p = this.preferedCountries.reverse();
41557 for (var i = 0; i < p.length; i++) {
41558 for (var j = 0; j < this.allCountries.length; j++) {
41559 if(this.allCountries[j].iso2 == p[i]) {
41560 var t = this.allCountries[j];
41561 this.allCountries.splice(j,1);
41562 this.allCountries.unshift(t);
41568 this.store.proxy.data = {
41570 data: this.allCountries
41576 initEvents : function()
41579 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41581 this.indicator = this.indicatorEl();
41582 this.flag = this.flagEl();
41583 this.dialCodeHolder = this.dialCodeHolderEl();
41585 this.trigger = this.el.select('div.flag-box',true).first();
41586 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41591 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41592 _this.list.setWidth(lw);
41595 this.list.on('mouseover', this.onViewOver, this);
41596 this.list.on('mousemove', this.onViewMove, this);
41597 this.inputEl().on("keyup", this.onKeyUp, this);
41598 this.inputEl().on("keypress", this.onKeyPress, this);
41600 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41602 this.view = new Roo.View(this.list, this.tpl, {
41603 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41606 this.view.on('click', this.onViewClick, this);
41607 this.setValue(this.defaultDialCode);
41610 onTriggerClick : function(e)
41612 Roo.log('trigger click');
41617 if(this.isExpanded()){
41619 this.hasFocus = false;
41621 this.store.load({});
41622 this.hasFocus = true;
41627 isExpanded : function()
41629 return this.list.isVisible();
41632 collapse : function()
41634 if(!this.isExpanded()){
41638 Roo.get(document).un('mousedown', this.collapseIf, this);
41639 Roo.get(document).un('mousewheel', this.collapseIf, this);
41640 this.fireEvent('collapse', this);
41644 expand : function()
41648 if(this.isExpanded() || !this.hasFocus){
41652 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41653 this.list.setWidth(lw);
41656 this.restrictHeight();
41658 Roo.get(document).on('mousedown', this.collapseIf, this);
41659 Roo.get(document).on('mousewheel', this.collapseIf, this);
41661 this.fireEvent('expand', this);
41664 restrictHeight : function()
41666 this.list.alignTo(this.inputEl(), this.listAlign);
41667 this.list.alignTo(this.inputEl(), this.listAlign);
41670 onViewOver : function(e, t)
41672 if(this.inKeyMode){
41675 var item = this.view.findItemFromChild(t);
41678 var index = this.view.indexOf(item);
41679 this.select(index, false);
41684 onViewClick : function(view, doFocus, el, e)
41686 var index = this.view.getSelectedIndexes()[0];
41688 var r = this.store.getAt(index);
41691 this.onSelect(r, index);
41693 if(doFocus !== false && !this.blockFocus){
41694 this.inputEl().focus();
41698 onViewMove : function(e, t)
41700 this.inKeyMode = false;
41703 select : function(index, scrollIntoView)
41705 this.selectedIndex = index;
41706 this.view.select(index);
41707 if(scrollIntoView !== false){
41708 var el = this.view.getNode(index);
41710 this.list.scrollChildIntoView(el, false);
41715 createList : function()
41717 this.list = Roo.get(document.body).createChild({
41719 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41720 style: 'display:none'
41723 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41726 collapseIf : function(e)
41728 var in_combo = e.within(this.el);
41729 var in_list = e.within(this.list);
41730 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41732 if (in_combo || in_list || is_list) {
41738 onSelect : function(record, index)
41740 if(this.fireEvent('beforeselect', this, record, index) !== false){
41742 this.setFlagClass(record.data.iso2);
41743 this.setDialCode(record.data.dialCode);
41744 this.hasFocus = false;
41746 this.fireEvent('select', this, record, index);
41750 flagEl : function()
41752 var flag = this.el.select('div.flag',true).first();
41759 dialCodeHolderEl : function()
41761 var d = this.el.select('input.dial-code-holder',true).first();
41768 setDialCode : function(v)
41770 this.dialCodeHolder.dom.value = '+'+v;
41773 setFlagClass : function(n)
41775 this.flag.dom.className = 'flag '+n;
41778 getValue : function()
41780 var v = this.inputEl().getValue();
41781 if(this.dialCodeHolder) {
41782 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41787 setValue : function(v)
41789 var d = this.getDialCode(v);
41791 //invalid dial code
41792 if(v.length == 0 || !d || d.length == 0) {
41794 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41795 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41801 this.setFlagClass(this.dialCodeMapping[d].iso2);
41802 this.setDialCode(d);
41803 this.inputEl().dom.value = v.replace('+'+d,'');
41804 this.hiddenEl().dom.value = this.getValue();
41809 getDialCode : function(v)
41813 if (v.length == 0) {
41814 return this.dialCodeHolder.dom.value;
41818 if (v.charAt(0) != "+") {
41821 var numericChars = "";
41822 for (var i = 1; i < v.length; i++) {
41823 var c = v.charAt(i);
41826 if (this.dialCodeMapping[numericChars]) {
41827 dialCode = v.substr(1, i);
41829 if (numericChars.length == 4) {
41839 this.setValue(this.defaultDialCode);
41843 hiddenEl : function()
41845 return this.el.select('input.hidden-tel-input',true).first();
41848 // after setting val
41849 onKeyUp : function(e){
41850 this.setValue(this.getValue());
41853 onKeyPress : function(e){
41854 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41861 * @class Roo.bootstrap.MoneyField
41862 * @extends Roo.bootstrap.ComboBox
41863 * Bootstrap MoneyField class
41866 * Create a new MoneyField.
41867 * @param {Object} config Configuration options
41870 Roo.bootstrap.MoneyField = function(config) {
41872 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41876 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41879 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41881 allowDecimals : true,
41883 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41885 decimalSeparator : ".",
41887 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41889 decimalPrecision : 0,
41891 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41893 allowNegative : true,
41895 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41899 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41901 minValue : Number.NEGATIVE_INFINITY,
41903 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41905 maxValue : Number.MAX_VALUE,
41907 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41909 minText : "The minimum value for this field is {0}",
41911 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41913 maxText : "The maximum value for this field is {0}",
41915 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41916 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41918 nanText : "{0} is not a valid number",
41920 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41924 * @cfg {String} defaults currency of the MoneyField
41925 * value should be in lkey
41927 defaultCurrency : false,
41929 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41931 thousandsDelimiter : false,
41933 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41944 getAutoCreate : function()
41946 var align = this.labelAlign || this.parentLabelAlign();
41958 cls : 'form-control roo-money-amount-input',
41959 autocomplete: 'new-password'
41962 var hiddenInput = {
41966 cls: 'hidden-number-input'
41969 if(this.max_length) {
41970 input.maxlength = this.max_length;
41974 hiddenInput.name = this.name;
41977 if (this.disabled) {
41978 input.disabled = true;
41981 var clg = 12 - this.inputlg;
41982 var cmd = 12 - this.inputmd;
41983 var csm = 12 - this.inputsm;
41984 var cxs = 12 - this.inputxs;
41988 cls : 'row roo-money-field',
41992 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41996 cls: 'roo-select2-container input-group',
42000 cls : 'form-control roo-money-currency-input',
42001 autocomplete: 'new-password',
42003 name : this.currencyName
42007 cls : 'input-group-addon',
42021 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42025 cls: this.hasFeedback ? 'has-feedback' : '',
42036 if (this.fieldLabel.length) {
42039 tooltip: 'This field is required'
42045 cls: 'control-label',
42051 html: this.fieldLabel
42054 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42060 if(this.indicatorpos == 'right') {
42061 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42068 if(align == 'left') {
42076 if(this.labelWidth > 12){
42077 label.style = "width: " + this.labelWidth + 'px';
42079 if(this.labelWidth < 13 && this.labelmd == 0){
42080 this.labelmd = this.labelWidth;
42082 if(this.labellg > 0){
42083 label.cls += ' col-lg-' + this.labellg;
42084 input.cls += ' col-lg-' + (12 - this.labellg);
42086 if(this.labelmd > 0){
42087 label.cls += ' col-md-' + this.labelmd;
42088 container.cls += ' col-md-' + (12 - this.labelmd);
42090 if(this.labelsm > 0){
42091 label.cls += ' col-sm-' + this.labelsm;
42092 container.cls += ' col-sm-' + (12 - this.labelsm);
42094 if(this.labelxs > 0){
42095 label.cls += ' col-xs-' + this.labelxs;
42096 container.cls += ' col-xs-' + (12 - this.labelxs);
42107 var settings = this;
42109 ['xs','sm','md','lg'].map(function(size){
42110 if (settings[size]) {
42111 cfg.cls += ' col-' + size + '-' + settings[size];
42118 initEvents : function()
42120 this.indicator = this.indicatorEl();
42122 this.initCurrencyEvent();
42124 this.initNumberEvent();
42127 initCurrencyEvent : function()
42130 throw "can not find store for combo";
42133 this.store = Roo.factory(this.store, Roo.data);
42134 this.store.parent = this;
42138 this.triggerEl = this.el.select('.input-group-addon', true).first();
42140 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42145 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42146 _this.list.setWidth(lw);
42149 this.list.on('mouseover', this.onViewOver, this);
42150 this.list.on('mousemove', this.onViewMove, this);
42151 this.list.on('scroll', this.onViewScroll, this);
42154 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42157 this.view = new Roo.View(this.list, this.tpl, {
42158 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42161 this.view.on('click', this.onViewClick, this);
42163 this.store.on('beforeload', this.onBeforeLoad, this);
42164 this.store.on('load', this.onLoad, this);
42165 this.store.on('loadexception', this.onLoadException, this);
42167 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42168 "up" : function(e){
42169 this.inKeyMode = true;
42173 "down" : function(e){
42174 if(!this.isExpanded()){
42175 this.onTriggerClick();
42177 this.inKeyMode = true;
42182 "enter" : function(e){
42185 if(this.fireEvent("specialkey", this, e)){
42186 this.onViewClick(false);
42192 "esc" : function(e){
42196 "tab" : function(e){
42199 if(this.fireEvent("specialkey", this, e)){
42200 this.onViewClick(false);
42208 doRelay : function(foo, bar, hname){
42209 if(hname == 'down' || this.scope.isExpanded()){
42210 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42218 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42222 initNumberEvent : function(e)
42224 this.inputEl().on("keydown" , this.fireKey, this);
42225 this.inputEl().on("focus", this.onFocus, this);
42226 this.inputEl().on("blur", this.onBlur, this);
42228 this.inputEl().relayEvent('keyup', this);
42230 if(this.indicator){
42231 this.indicator.addClass('invisible');
42234 this.originalValue = this.getValue();
42236 if(this.validationEvent == 'keyup'){
42237 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42238 this.inputEl().on('keyup', this.filterValidation, this);
42240 else if(this.validationEvent !== false){
42241 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42244 if(this.selectOnFocus){
42245 this.on("focus", this.preFocus, this);
42248 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42249 this.inputEl().on("keypress", this.filterKeys, this);
42251 this.inputEl().relayEvent('keypress', this);
42254 var allowed = "0123456789";
42256 if(this.allowDecimals){
42257 allowed += this.decimalSeparator;
42260 if(this.allowNegative){
42264 if(this.thousandsDelimiter) {
42268 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42270 var keyPress = function(e){
42272 var k = e.getKey();
42274 var c = e.getCharCode();
42277 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42278 allowed.indexOf(String.fromCharCode(c)) === -1
42284 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42288 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42293 this.inputEl().on("keypress", keyPress, this);
42297 onTriggerClick : function(e)
42304 this.loadNext = false;
42306 if(this.isExpanded()){
42311 this.hasFocus = true;
42313 if(this.triggerAction == 'all') {
42314 this.doQuery(this.allQuery, true);
42318 this.doQuery(this.getRawValue());
42321 getCurrency : function()
42323 var v = this.currencyEl().getValue();
42328 restrictHeight : function()
42330 this.list.alignTo(this.currencyEl(), this.listAlign);
42331 this.list.alignTo(this.currencyEl(), this.listAlign);
42334 onViewClick : function(view, doFocus, el, e)
42336 var index = this.view.getSelectedIndexes()[0];
42338 var r = this.store.getAt(index);
42341 this.onSelect(r, index);
42345 onSelect : function(record, index){
42347 if(this.fireEvent('beforeselect', this, record, index) !== false){
42349 this.setFromCurrencyData(index > -1 ? record.data : false);
42353 this.fireEvent('select', this, record, index);
42357 setFromCurrencyData : function(o)
42361 this.lastCurrency = o;
42363 if (this.currencyField) {
42364 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42366 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42369 this.lastSelectionText = currency;
42371 //setting default currency
42372 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42373 this.setCurrency(this.defaultCurrency);
42377 this.setCurrency(currency);
42380 setFromData : function(o)
42384 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42386 this.setFromCurrencyData(c);
42391 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42393 Roo.log('no value set for '+ (this.name ? this.name : this.id));
42396 this.setValue(value);
42400 setCurrency : function(v)
42402 this.currencyValue = v;
42405 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42410 setValue : function(v)
42412 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42418 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42420 this.inputEl().dom.value = (v == '') ? '' :
42421 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42423 if(!this.allowZero && v === '0') {
42424 this.hiddenEl().dom.value = '';
42425 this.inputEl().dom.value = '';
42432 getRawValue : function()
42434 var v = this.inputEl().getValue();
42439 getValue : function()
42441 return this.fixPrecision(this.parseValue(this.getRawValue()));
42444 parseValue : function(value)
42446 if(this.thousandsDelimiter) {
42448 r = new RegExp(",", "g");
42449 value = value.replace(r, "");
42452 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42453 return isNaN(value) ? '' : value;
42457 fixPrecision : function(value)
42459 if(this.thousandsDelimiter) {
42461 r = new RegExp(",", "g");
42462 value = value.replace(r, "");
42465 var nan = isNaN(value);
42467 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42468 return nan ? '' : value;
42470 return parseFloat(value).toFixed(this.decimalPrecision);
42473 decimalPrecisionFcn : function(v)
42475 return Math.floor(v);
42478 validateValue : function(value)
42480 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42484 var num = this.parseValue(value);
42487 this.markInvalid(String.format(this.nanText, value));
42491 if(num < this.minValue){
42492 this.markInvalid(String.format(this.minText, this.minValue));
42496 if(num > this.maxValue){
42497 this.markInvalid(String.format(this.maxText, this.maxValue));
42504 validate : function()
42506 if(this.disabled || this.allowBlank){
42511 var currency = this.getCurrency();
42513 if(this.validateValue(this.getRawValue()) && currency.length){
42518 this.markInvalid();
42522 getName: function()
42527 beforeBlur : function()
42533 var v = this.parseValue(this.getRawValue());
42540 onBlur : function()
42544 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42545 //this.el.removeClass(this.focusClass);
42548 this.hasFocus = false;
42550 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42554 var v = this.getValue();
42556 if(String(v) !== String(this.startValue)){
42557 this.fireEvent('change', this, v, this.startValue);
42560 this.fireEvent("blur", this);
42563 inputEl : function()
42565 return this.el.select('.roo-money-amount-input', true).first();
42568 currencyEl : function()
42570 return this.el.select('.roo-money-currency-input', true).first();
42573 hiddenEl : function()
42575 return this.el.select('input.hidden-number-input',true).first();
42579 * @class Roo.bootstrap.BezierSignature
42580 * @extends Roo.bootstrap.Component
42581 * Bootstrap BezierSignature class
42582 * This script refer to:
42583 * Title: Signature Pad
42585 * Availability: https://github.com/szimek/signature_pad
42588 * Create a new BezierSignature
42589 * @param {Object} config The config object
42592 Roo.bootstrap.BezierSignature = function(config){
42593 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42599 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42606 mouse_btn_down: true,
42609 * @cfg {int} canvas height
42611 canvas_height: '200px',
42614 * @cfg {float|function} Radius of a single dot.
42619 * @cfg {float} Minimum width of a line. Defaults to 0.5.
42624 * @cfg {float} Maximum width of a line. Defaults to 2.5.
42629 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42634 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42639 * @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.
42641 bg_color: 'rgba(0, 0, 0, 0)',
42644 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42646 dot_color: 'black',
42649 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42651 velocity_filter_weight: 0.7,
42654 * @cfg {function} Callback when stroke begin.
42659 * @cfg {function} Callback when stroke end.
42663 getAutoCreate : function()
42665 var cls = 'roo-signature column';
42668 cls += ' ' + this.cls;
42678 for(var i = 0; i < col_sizes.length; i++) {
42679 if(this[col_sizes[i]]) {
42680 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42690 cls: 'roo-signature-body',
42694 cls: 'roo-signature-body-canvas',
42695 height: this.canvas_height,
42696 width: this.canvas_width
42703 style: 'display: none'
42711 initEvents: function()
42713 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42715 var canvas = this.canvasEl();
42717 // mouse && touch event swapping...
42718 canvas.dom.style.touchAction = 'none';
42719 canvas.dom.style.msTouchAction = 'none';
42721 this.mouse_btn_down = false;
42722 canvas.on('mousedown', this._handleMouseDown, this);
42723 canvas.on('mousemove', this._handleMouseMove, this);
42724 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42726 if (window.PointerEvent) {
42727 canvas.on('pointerdown', this._handleMouseDown, this);
42728 canvas.on('pointermove', this._handleMouseMove, this);
42729 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42732 if ('ontouchstart' in window) {
42733 canvas.on('touchstart', this._handleTouchStart, this);
42734 canvas.on('touchmove', this._handleTouchMove, this);
42735 canvas.on('touchend', this._handleTouchEnd, this);
42738 Roo.EventManager.onWindowResize(this.resize, this, true);
42740 // file input event
42741 this.fileEl().on('change', this.uploadImage, this);
42748 resize: function(){
42750 var canvas = this.canvasEl().dom;
42751 var ctx = this.canvasElCtx();
42752 var img_data = false;
42754 if(canvas.width > 0) {
42755 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42757 // setting canvas width will clean img data
42760 var style = window.getComputedStyle ?
42761 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42763 var padding_left = parseInt(style.paddingLeft) || 0;
42764 var padding_right = parseInt(style.paddingRight) || 0;
42766 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42769 ctx.putImageData(img_data, 0, 0);
42773 _handleMouseDown: function(e)
42775 if (e.browserEvent.which === 1) {
42776 this.mouse_btn_down = true;
42777 this.strokeBegin(e);
42781 _handleMouseMove: function (e)
42783 if (this.mouse_btn_down) {
42784 this.strokeMoveUpdate(e);
42788 _handleMouseUp: function (e)
42790 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42791 this.mouse_btn_down = false;
42796 _handleTouchStart: function (e) {
42798 e.preventDefault();
42799 if (e.browserEvent.targetTouches.length === 1) {
42800 // var touch = e.browserEvent.changedTouches[0];
42801 // this.strokeBegin(touch);
42803 this.strokeBegin(e); // assume e catching the correct xy...
42807 _handleTouchMove: function (e) {
42808 e.preventDefault();
42809 // var touch = event.targetTouches[0];
42810 // _this._strokeMoveUpdate(touch);
42811 this.strokeMoveUpdate(e);
42814 _handleTouchEnd: function (e) {
42815 var wasCanvasTouched = e.target === this.canvasEl().dom;
42816 if (wasCanvasTouched) {
42817 e.preventDefault();
42818 // var touch = event.changedTouches[0];
42819 // _this._strokeEnd(touch);
42824 reset: function () {
42825 this._lastPoints = [];
42826 this._lastVelocity = 0;
42827 this._lastWidth = (this.min_width + this.max_width) / 2;
42828 this.canvasElCtx().fillStyle = this.dot_color;
42831 strokeMoveUpdate: function(e)
42833 this.strokeUpdate(e);
42835 if (this.throttle) {
42836 this.throttleStroke(this.strokeUpdate, this.throttle);
42839 this.strokeUpdate(e);
42843 strokeBegin: function(e)
42845 var newPointGroup = {
42846 color: this.dot_color,
42850 if (typeof this.onBegin === 'function') {
42854 this.curve_data.push(newPointGroup);
42856 this.strokeUpdate(e);
42859 strokeUpdate: function(e)
42861 var rect = this.canvasEl().dom.getBoundingClientRect();
42862 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42863 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42864 var lastPoints = lastPointGroup.points;
42865 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42866 var isLastPointTooClose = lastPoint
42867 ? point.distanceTo(lastPoint) <= this.min_distance
42869 var color = lastPointGroup.color;
42870 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42871 var curve = this.addPoint(point);
42873 this.drawDot({color: color, point: point});
42876 this.drawCurve({color: color, curve: curve});
42886 strokeEnd: function(e)
42888 this.strokeUpdate(e);
42889 if (typeof this.onEnd === 'function') {
42894 addPoint: function (point) {
42895 var _lastPoints = this._lastPoints;
42896 _lastPoints.push(point);
42897 if (_lastPoints.length > 2) {
42898 if (_lastPoints.length === 3) {
42899 _lastPoints.unshift(_lastPoints[0]);
42901 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42902 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42903 _lastPoints.shift();
42909 calculateCurveWidths: function (startPoint, endPoint) {
42910 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42911 (1 - this.velocity_filter_weight) * this._lastVelocity;
42913 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42916 start: this._lastWidth
42919 this._lastVelocity = velocity;
42920 this._lastWidth = newWidth;
42924 drawDot: function (_a) {
42925 var color = _a.color, point = _a.point;
42926 var ctx = this.canvasElCtx();
42927 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42929 this.drawCurveSegment(point.x, point.y, width);
42931 ctx.fillStyle = color;
42935 drawCurve: function (_a) {
42936 var color = _a.color, curve = _a.curve;
42937 var ctx = this.canvasElCtx();
42938 var widthDelta = curve.endWidth - curve.startWidth;
42939 var drawSteps = Math.floor(curve.length()) * 2;
42941 ctx.fillStyle = color;
42942 for (var i = 0; i < drawSteps; i += 1) {
42943 var t = i / drawSteps;
42949 var x = uuu * curve.startPoint.x;
42950 x += 3 * uu * t * curve.control1.x;
42951 x += 3 * u * tt * curve.control2.x;
42952 x += ttt * curve.endPoint.x;
42953 var y = uuu * curve.startPoint.y;
42954 y += 3 * uu * t * curve.control1.y;
42955 y += 3 * u * tt * curve.control2.y;
42956 y += ttt * curve.endPoint.y;
42957 var width = curve.startWidth + ttt * widthDelta;
42958 this.drawCurveSegment(x, y, width);
42964 drawCurveSegment: function (x, y, width) {
42965 var ctx = this.canvasElCtx();
42967 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42968 this.is_empty = false;
42973 var ctx = this.canvasElCtx();
42974 var canvas = this.canvasEl().dom;
42975 ctx.fillStyle = this.bg_color;
42976 ctx.clearRect(0, 0, canvas.width, canvas.height);
42977 ctx.fillRect(0, 0, canvas.width, canvas.height);
42978 this.curve_data = [];
42980 this.is_empty = true;
42985 return this.el.select('input',true).first();
42988 canvasEl: function()
42990 return this.el.select('canvas',true).first();
42993 canvasElCtx: function()
42995 return this.el.select('canvas',true).first().dom.getContext('2d');
42998 getImage: function(type)
43000 if(this.is_empty) {
43005 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43008 drawFromImage: function(img_src)
43010 var img = new Image();
43012 img.onload = function(){
43013 this.canvasElCtx().drawImage(img, 0, 0);
43018 this.is_empty = false;
43021 selectImage: function()
43023 this.fileEl().dom.click();
43026 uploadImage: function(e)
43028 var reader = new FileReader();
43030 reader.onload = function(e){
43031 var img = new Image();
43032 img.onload = function(){
43034 this.canvasElCtx().drawImage(img, 0, 0);
43036 img.src = e.target.result;
43039 reader.readAsDataURL(e.target.files[0]);
43042 // Bezier Point Constructor
43043 Point: (function () {
43044 function Point(x, y, time) {
43047 this.time = time || Date.now();
43049 Point.prototype.distanceTo = function (start) {
43050 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43052 Point.prototype.equals = function (other) {
43053 return this.x === other.x && this.y === other.y && this.time === other.time;
43055 Point.prototype.velocityFrom = function (start) {
43056 return this.time !== start.time
43057 ? this.distanceTo(start) / (this.time - start.time)
43064 // Bezier Constructor
43065 Bezier: (function () {
43066 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43067 this.startPoint = startPoint;
43068 this.control2 = control2;
43069 this.control1 = control1;
43070 this.endPoint = endPoint;
43071 this.startWidth = startWidth;
43072 this.endWidth = endWidth;
43074 Bezier.fromPoints = function (points, widths, scope) {
43075 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43076 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43077 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43079 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43080 var dx1 = s1.x - s2.x;
43081 var dy1 = s1.y - s2.y;
43082 var dx2 = s2.x - s3.x;
43083 var dy2 = s2.y - s3.y;
43084 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43085 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43086 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43087 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43088 var dxm = m1.x - m2.x;
43089 var dym = m1.y - m2.y;
43090 var k = l2 / (l1 + l2);
43091 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43092 var tx = s2.x - cm.x;
43093 var ty = s2.y - cm.y;
43095 c1: new scope.Point(m1.x + tx, m1.y + ty),
43096 c2: new scope.Point(m2.x + tx, m2.y + ty)
43099 Bezier.prototype.length = function () {
43104 for (var i = 0; i <= steps; i += 1) {
43106 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43107 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43109 var xdiff = cx - px;
43110 var ydiff = cy - py;
43111 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43118 Bezier.prototype.point = function (t, start, c1, c2, end) {
43119 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43120 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43121 + (3.0 * c2 * (1.0 - t) * t * t)
43122 + (end * t * t * t);
43127 throttleStroke: function(fn, wait) {
43128 if (wait === void 0) { wait = 250; }
43130 var timeout = null;
43134 var later = function () {
43135 previous = Date.now();
43137 result = fn.apply(storedContext, storedArgs);
43139 storedContext = null;
43143 return function wrapper() {
43145 for (var _i = 0; _i < arguments.length; _i++) {
43146 args[_i] = arguments[_i];
43148 var now = Date.now();
43149 var remaining = wait - (now - previous);
43150 storedContext = this;
43152 if (remaining <= 0 || remaining > wait) {
43154 clearTimeout(timeout);
43158 result = fn.apply(storedContext, storedArgs);
43160 storedContext = null;
43164 else if (!timeout) {
43165 timeout = window.setTimeout(later, remaining);