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|light|dark) default
965 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) 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 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 (_self|_blank|_parent|_top|other) target for a href.
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);
998 * When a butotn is pressed
999 * @param {Roo.bootstrap.Button} btn
1000 * @param {Roo.EventObject} e
1005 * After the button has been toggles
1006 * @param {Roo.bootstrap.Button} btn
1007 * @param {Roo.EventObject} e
1008 * @param {boolean} pressed (also available as button.pressed)
1014 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1035 preventDefault: true,
1043 getAutoCreate : function(){
1051 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1052 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1053 this.tag = 'button';
1057 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1059 if (this.toggle == true) {
1062 cls: 'slider-frame roo-button',
1066 'data-on-text':'ON',
1067 'data-off-text':'OFF',
1068 cls: 'slider-button',
1073 // why are we validating the weights?
1074 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1075 cfg.cls += ' ' + this.weight;
1082 cfg.cls += ' close';
1084 cfg["aria-hidden"] = true;
1086 cfg.html = "×";
1092 if (this.theme==='default') {
1093 cfg.cls = 'btn roo-button';
1095 //if (this.parentType != 'Navbar') {
1096 this.weight = this.weight.length ? this.weight : 'default';
1098 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1100 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1101 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1102 cfg.cls += ' btn-' + outline + weight;
1103 if (this.weight == 'default') {
1105 cfg.cls += ' btn-' + this.weight;
1108 } else if (this.theme==='glow') {
1111 cfg.cls = 'btn-glow roo-button';
1113 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1115 cfg.cls += ' ' + this.weight;
1121 this.cls += ' inverse';
1125 if (this.active || this.pressed === true) {
1126 cfg.cls += ' active';
1129 if (this.disabled) {
1130 cfg.disabled = 'disabled';
1134 Roo.log('changing to ul' );
1136 this.glyphicon = 'caret';
1137 if (Roo.bootstrap.version == 4) {
1138 this.fa = 'caret-down';
1143 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1145 //gsRoo.log(this.parentType);
1146 if (this.parentType === 'Navbar' && !this.parent().bar) {
1147 Roo.log('changing to li?');
1156 href : this.href || '#'
1159 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1160 cfg.cls += ' dropdown';
1167 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1169 if (this.glyphicon) {
1170 cfg.html = ' ' + cfg.html;
1175 cls: 'glyphicon glyphicon-' + this.glyphicon
1180 cfg.html = ' ' + cfg.html;
1185 cls: 'fa fas fa-' + this.fa
1195 // cfg.cls='btn roo-button';
1199 var value = cfg.html;
1204 cls: 'glyphicon glyphicon-' + this.glyphicon,
1211 cls: 'fa fas fa-' + this.fa,
1216 var bw = this.badge_weight.length ? this.badge_weight :
1217 (this.weight.length ? this.weight : 'secondary');
1218 bw = bw == 'default' ? 'secondary' : bw;
1224 cls: 'badge badge-' + bw,
1233 cfg.cls += ' dropdown';
1234 cfg.html = typeof(cfg.html) != 'undefined' ?
1235 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1238 if (cfg.tag !== 'a' && this.href !== '') {
1239 throw "Tag must be a to set href.";
1240 } else if (this.href.length > 0) {
1241 cfg.href = this.href;
1244 if(this.removeClass){
1249 cfg.target = this.target;
1254 initEvents: function() {
1255 // Roo.log('init events?');
1256 // Roo.log(this.el.dom);
1259 if (typeof (this.menu) != 'undefined') {
1260 this.menu.parentType = this.xtype;
1261 this.menu.triggerEl = this.el;
1262 this.addxtype(Roo.apply({}, this.menu));
1266 if (this.el.hasClass('roo-button')) {
1267 this.el.on('click', this.onClick, this);
1269 this.el.select('.roo-button').on('click', this.onClick, this);
1272 if(this.removeClass){
1273 this.el.on('click', this.onClick, this);
1276 this.el.enableDisplayMode();
1279 onClick : function(e)
1281 if (this.disabled) {
1285 Roo.log('button on click ');
1286 if(this.preventDefault){
1290 if (this.pressed === true || this.pressed === false) {
1291 this.toggleActive(e);
1295 this.fireEvent('click', this, e);
1299 * Enables this button
1303 this.disabled = false;
1304 this.el.removeClass('disabled');
1308 * Disable this button
1310 disable : function()
1312 this.disabled = true;
1313 this.el.addClass('disabled');
1316 * sets the active state on/off,
1317 * @param {Boolean} state (optional) Force a particular state
1319 setActive : function(v) {
1321 this.el[v ? 'addClass' : 'removeClass']('active');
1325 * toggles the current active state
1327 toggleActive : function(e)
1329 this.setActive(!this.pressed);
1330 this.fireEvent('toggle', this, e, !this.pressed);
1333 * get the current active state
1334 * @return {boolean} true if it's active
1336 isActive : function()
1338 return this.el.hasClass('active');
1341 * set the text of the first selected button
1343 setText : function(str)
1345 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1348 * get the text of the first selected button
1350 getText : function()
1352 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1355 setWeight : function(str)
1357 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1358 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1360 var outline = this.outline ? 'outline-' : '';
1361 if (str == 'default') {
1362 this.el.addClass('btn-default btn-outline-secondary');
1365 this.el.addClass('btn-' + outline + str);
1370 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1372 Roo.bootstrap.Button.weights = [
1392 * @class Roo.bootstrap.Column
1393 * @extends Roo.bootstrap.Component
1394 * Bootstrap Column class
1395 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1396 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1397 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1398 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1399 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1400 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1401 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1402 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1405 * @cfg {Boolean} hidden (true|false) hide the element
1406 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1407 * @cfg {String} fa (ban|check|...) font awesome icon
1408 * @cfg {Number} fasize (1|2|....) font awsome size
1410 * @cfg {String} icon (info-sign|check|...) glyphicon name
1412 * @cfg {String} html content of column.
1415 * Create a new Column
1416 * @param {Object} config The config object
1419 Roo.bootstrap.Column = function(config){
1420 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1423 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1441 getAutoCreate : function(){
1442 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1450 var sizes = ['xs','sm','md','lg'];
1451 sizes.map(function(size ,ix){
1452 //Roo.log( size + ':' + settings[size]);
1454 if (settings[size+'off'] !== false) {
1455 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1458 if (settings[size] === false) {
1462 if (!settings[size]) { // 0 = hidden
1463 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1465 for (var i = ix; i > -1; i--) {
1466 cfg.cls += ' d-' + sizes[i] + '-none';
1472 cfg.cls += ' col-' + size + '-' + settings[size] + (
1473 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1479 cfg.cls += ' hidden';
1482 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1483 cfg.cls +=' alert alert-' + this.alert;
1487 if (this.html.length) {
1488 cfg.html = this.html;
1492 if (this.fasize > 1) {
1493 fasize = ' fa-' + this.fasize + 'x';
1495 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1500 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1519 * @class Roo.bootstrap.Container
1520 * @extends Roo.bootstrap.Component
1521 * Bootstrap Container class
1522 * @cfg {Boolean} jumbotron is it a jumbotron element
1523 * @cfg {String} html content of element
1524 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1525 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1526 * @cfg {String} header content of header (for panel)
1527 * @cfg {String} footer content of footer (for panel)
1528 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1529 * @cfg {String} tag (header|aside|section) type of HTML tag.
1530 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1531 * @cfg {String} fa font awesome icon
1532 * @cfg {String} icon (info-sign|check|...) glyphicon name
1533 * @cfg {Boolean} hidden (true|false) hide the element
1534 * @cfg {Boolean} expandable (true|false) default false
1535 * @cfg {Boolean} expanded (true|false) default true
1536 * @cfg {String} rheader contet on the right of header
1537 * @cfg {Boolean} clickable (true|false) default false
1541 * Create a new Container
1542 * @param {Object} config The config object
1545 Roo.bootstrap.Container = function(config){
1546 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1552 * After the panel has been expand
1554 * @param {Roo.bootstrap.Container} this
1559 * After the panel has been collapsed
1561 * @param {Roo.bootstrap.Container} this
1566 * When a element is chick
1567 * @param {Roo.bootstrap.Container} this
1568 * @param {Roo.EventObject} e
1574 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1592 getChildContainer : function() {
1598 if (this.panel.length) {
1599 return this.el.select('.panel-body',true).first();
1606 getAutoCreate : function(){
1609 tag : this.tag || 'div',
1613 if (this.jumbotron) {
1614 cfg.cls = 'jumbotron';
1619 // - this is applied by the parent..
1621 // cfg.cls = this.cls + '';
1624 if (this.sticky.length) {
1626 var bd = Roo.get(document.body);
1627 if (!bd.hasClass('bootstrap-sticky')) {
1628 bd.addClass('bootstrap-sticky');
1629 Roo.select('html',true).setStyle('height', '100%');
1632 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1636 if (this.well.length) {
1637 switch (this.well) {
1640 cfg.cls +=' well well-' +this.well;
1649 cfg.cls += ' hidden';
1653 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1654 cfg.cls +=' alert alert-' + this.alert;
1659 if (this.panel.length) {
1660 cfg.cls += ' panel panel-' + this.panel;
1662 if (this.header.length) {
1666 if(this.expandable){
1668 cfg.cls = cfg.cls + ' expandable';
1672 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1680 cls : 'panel-title',
1681 html : (this.expandable ? ' ' : '') + this.header
1685 cls: 'panel-header-right',
1691 cls : 'panel-heading',
1692 style : this.expandable ? 'cursor: pointer' : '',
1700 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1705 if (this.footer.length) {
1707 cls : 'panel-footer',
1716 body.html = this.html || cfg.html;
1717 // prefix with the icons..
1719 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1722 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1727 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1728 cfg.cls = 'container';
1734 initEvents: function()
1736 if(this.expandable){
1737 var headerEl = this.headerEl();
1740 headerEl.on('click', this.onToggleClick, this);
1745 this.el.on('click', this.onClick, this);
1750 onToggleClick : function()
1752 var headerEl = this.headerEl();
1768 if(this.fireEvent('expand', this)) {
1770 this.expanded = true;
1772 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1774 this.el.select('.panel-body',true).first().removeClass('hide');
1776 var toggleEl = this.toggleEl();
1782 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1787 collapse : function()
1789 if(this.fireEvent('collapse', this)) {
1791 this.expanded = false;
1793 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1794 this.el.select('.panel-body',true).first().addClass('hide');
1796 var toggleEl = this.toggleEl();
1802 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1806 toggleEl : function()
1808 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1812 return this.el.select('.panel-heading .fa',true).first();
1815 headerEl : function()
1817 if(!this.el || !this.panel.length || !this.header.length){
1821 return this.el.select('.panel-heading',true).first()
1826 if(!this.el || !this.panel.length){
1830 return this.el.select('.panel-body',true).first()
1833 titleEl : function()
1835 if(!this.el || !this.panel.length || !this.header.length){
1839 return this.el.select('.panel-title',true).first();
1842 setTitle : function(v)
1844 var titleEl = this.titleEl();
1850 titleEl.dom.innerHTML = v;
1853 getTitle : function()
1856 var titleEl = this.titleEl();
1862 return titleEl.dom.innerHTML;
1865 setRightTitle : function(v)
1867 var t = this.el.select('.panel-header-right',true).first();
1873 t.dom.innerHTML = v;
1876 onClick : function(e)
1880 this.fireEvent('click', this, e);
1887 * This is BS4's Card element.. - similar to our containers probably..
1891 * @class Roo.bootstrap.Card
1892 * @extends Roo.bootstrap.Component
1893 * Bootstrap Card class
1896 * possible... may not be implemented..
1897 * @cfg {String} header_image src url of image.
1898 * @cfg {String|Object} header
1899 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1901 * @cfg {String} title
1902 * @cfg {String} subtitle
1903 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1904 * @cfg {String} footer
1906 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1908 * @cfg {String} margin (0|1|2|3|4|5|auto)
1909 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1910 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1911 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1912 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1913 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1914 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1916 * @cfg {String} padding (0|1|2|3|4|5)
1917 * @cfg {String} padding_top (0|1|2|3|4|5)
1918 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1919 * @cfg {String} padding_left (0|1|2|3|4|5)
1920 * @cfg {String} padding_right (0|1|2|3|4|5)
1921 * @cfg {String} padding_x (0|1|2|3|4|5)
1922 * @cfg {String} padding_y (0|1|2|3|4|5)
1924 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1930 * @config {Boolean} dragable if this card can be dragged.
1931 * @config {String} drag_group group for drag
1932 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1933 * @config {String} drop_group group for drag
1935 * @config {Boolean} collapsable can the body be collapsed.
1936 * @config {Boolean} collapsed is the body collapsed when rendered...
1937 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1938 * @config {Boolean} rotated is the body rotated when rendered...
1941 * Create a new Container
1942 * @param {Object} config The config object
1945 Roo.bootstrap.Card = function(config){
1946 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1952 * When a element a card is dropped
1953 * @param {Roo.bootstrap.Element} this
1954 * @param {Roo.Element} n the node being dropped?
1955 * @param {Object} dd Drag and drop data
1956 * @param {Roo.EventObject} e
1957 * @param {Roo.EventObject} data the data passed via getDragData
1962 * When a element a card is rotate
1963 * @param {Roo.bootstrap.Element} this
1964 * @param {Roo.Element} n the node being dropped?
1965 * @param {Boolean} rotate status
1973 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1978 margin: '', /// may be better in component?
2008 collapsable : false,
2017 childContainer : false,
2018 dropEl : false, /// the dom placeholde element that indicates drop location.
2020 layoutCls : function()
2024 Roo.log(this.margin_bottom.length);
2025 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2026 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2028 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2029 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2031 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2032 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2036 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2037 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2038 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2042 // more generic support?
2050 // Roo.log("Call onRender: " + this.xtype);
2051 /* We are looking at something like this.
2053 <img src="..." class="card-img-top" alt="...">
2054 <div class="card-body">
2055 <h5 class="card-title">Card title</h5>
2056 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2058 >> this bit is really the body...
2059 <div> << we will ad dthis in hopefully it will not break shit.
2061 ** card text does not actually have any styling...
2063 <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>
2066 <a href="#" class="card-link">Card link</a>
2069 <div class="card-footer">
2070 <small class="text-muted">Last updated 3 mins ago</small>
2074 getAutoCreate : function(){
2082 if (this.weight.length && this.weight != 'light') {
2083 cfg.cls += ' text-white';
2085 cfg.cls += ' text-dark'; // need as it's nested..
2087 if (this.weight.length) {
2088 cfg.cls += ' bg-' + this.weight;
2091 cfg.cls += this.layoutCls();
2094 if (this.header.length) {
2096 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2097 cls : 'card-header',
2105 cls : 'card-header d-none',
2110 if (this.collapsable) {
2113 cls : 'd-block user-select-none',
2117 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2122 hdr.cn.push(hdr_ctr);
2127 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2132 if (this.header_image.length) {
2135 cls : 'card-img-top',
2136 src: this.header_image // escape?
2141 cls : 'card-img-top d-none'
2147 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2151 if (this.collapsable || this.rotateable) {
2154 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2161 if (this.title.length) {
2165 src: this.title // escape?
2169 if (this.subtitle.length) {
2173 src: this.subtitle // escape?
2179 cls : 'roo-card-body-ctr'
2182 if (this.html.length) {
2188 // fixme ? handle objects?
2190 if (this.footer.length) {
2193 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2198 cfg.cn.push({cls : 'card-footer d-none'});
2207 getCardHeader : function()
2209 var ret = this.el.select('.card-header',true).first();
2210 if (ret.hasClass('d-none')) {
2211 ret.removeClass('d-none');
2216 getCardFooter : function()
2218 var ret = this.el.select('.card-footer',true).first();
2219 if (ret.hasClass('d-none')) {
2220 ret.removeClass('d-none');
2225 getCardImageTop : function()
2227 var ret = this.el.select('.card-img-top',true).first();
2228 if (ret.hasClass('d-none')) {
2229 ret.removeClass('d-none');
2235 getChildContainer : function()
2241 return this.el.select('.roo-card-body-ctr',true).first();
2244 initEvents: function()
2247 this.bodyEl = this.getChildContainer();
2249 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2250 containerScroll: true,
2251 ddGroup: this.drag_group || 'default_card_drag_group'
2253 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2255 if (this.dropable) {
2256 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2257 containerScroll: true,
2258 ddGroup: this.drop_group || 'default_card_drag_group'
2260 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2261 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2262 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2263 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2264 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2267 if (this.collapsable) {
2268 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2270 if (this.rotateable) {
2271 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2273 this.collapsableEl = this.el.select('.roo-collapsable').first();
2275 this.footerEl = this.el.select('.card-footer').first();
2276 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2277 this.headerEl = this.el.select('.roo-card-header-ctr').first();
2280 this.el.addClass('roo-card-rotated');
2281 this.fireEvent('rotate', this, true);
2285 getDragData : function(e)
2287 var target = this.getEl();
2289 //this.handleSelection(e);
2294 nodes: this.getEl(),
2299 dragData.ddel = target.dom ; // the div element
2300 Roo.log(target.getWidth( ));
2301 dragData.ddel.style.width = target.getWidth() + 'px';
2308 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2309 * whole Element becomes the target, and this causes the drop gesture to append.
2311 getTargetFromEvent : function(e, dragged_card_el)
2313 var target = e.getTarget();
2314 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2315 target = target.parentNode;
2326 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2327 // see if target is one of the 'cards'...
2330 //Roo.log(this.items.length);
2333 var last_card_n = 0;
2335 for (var i = 0;i< this.items.length;i++) {
2337 if (!this.items[i].el.hasClass('card')) {
2340 pos = this.getDropPoint(e, this.items[i].el.dom);
2342 cards_len = ret.cards.length;
2343 //Roo.log(this.items[i].el.dom.id);
2344 ret.cards.push(this.items[i]);
2346 if (ret.card_n < 0 && pos == 'above') {
2347 ret.position = cards_len > 0 ? 'below' : pos;
2348 ret.items_n = i > 0 ? i - 1 : 0;
2349 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2350 ret.card = ret.cards[ret.card_n];
2353 if (!ret.cards.length) {
2355 ret.position = 'below';
2359 // could not find a card.. stick it at the end..
2360 if (ret.card_n < 0) {
2361 ret.card_n = last_card_n;
2362 ret.card = ret.cards[last_card_n];
2363 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2364 ret.position = 'below';
2367 if (this.items[ret.items_n].el == dragged_card_el) {
2371 if (ret.position == 'below') {
2372 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2374 if (card_after && card_after.el == dragged_card_el) {
2381 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2383 if (card_before && card_before.el == dragged_card_el) {
2390 onNodeEnter : function(n, dd, e, data){
2393 onNodeOver : function(n, dd, e, data)
2396 var target_info = this.getTargetFromEvent(e,data.source.el);
2397 if (target_info === false) {
2398 this.dropPlaceHolder('hide');
2401 Roo.log(['getTargetFromEvent', target_info ]);
2404 this.dropPlaceHolder('show', target_info,data);
2408 onNodeOut : function(n, dd, e, data){
2409 this.dropPlaceHolder('hide');
2412 onNodeDrop : function(n, dd, e, data)
2415 // call drop - return false if
2417 // this could actually fail - if the Network drops..
2418 // we will ignore this at present..- client should probably reload
2419 // the whole set of cards if stuff like that fails.
2422 var info = this.getTargetFromEvent(e,data.source.el);
2423 if (info === false) {
2427 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2431 this.dropPlaceHolder('hide');
2433 // do the dom manipulation first..
2434 var dom = data.source.el.dom;
2435 dom.parentNode.removeChild(dom);
2438 if (info.card !== true) {
2439 var cardel = info.card.el.dom;
2441 if (info.position == 'above') {
2442 cardel.parentNode.insertBefore(dom, cardel);
2443 } else if (cardel.nextSibling) {
2444 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2446 cardel.parentNode.append(dom);
2449 // card container???
2450 this.bodyEl.dom.append(dom);
2453 //FIXME HANDLE card = true
2455 // add this to the correct place in items.
2459 // remove Card from items.
2461 var old_parent = data.source.parent();
2463 old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2465 if (this.items.length) {
2467 //Roo.log([info.items_n, info.position, this.items.length]);
2468 for (var i =0; i < this.items.length; i++) {
2469 if (i == info.items_n && info.position == 'above') {
2470 nitems.push(data.source);
2472 nitems.push(this.items[i]);
2473 if (i == info.items_n && info.position == 'below') {
2474 nitems.push(data.source);
2477 this.items = nitems;
2478 Roo.log(this.items);
2480 this.items.push(data.source);
2483 data.source.parentId = this.id;
2488 /** Decide whether to drop above or below a View node. */
2489 getDropPoint : function(e, n, dd)
2494 if (n == this.bodyEl.dom) {
2497 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2498 var c = t + (b - t) / 2;
2499 var y = Roo.lib.Event.getPageY(e);
2506 onToggleCollapse : function(e)
2508 if (this.collapsed) {
2509 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2510 this.collapsableEl.addClass('show');
2511 this.collapsed = false;
2514 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2515 this.collapsableEl.removeClass('show');
2516 this.collapsed = true;
2521 onToggleRotate : function(e)
2523 this.collapsableEl.removeClass('show');
2524 this.footerEl.removeClass('d-none');
2525 this.el.removeClass('roo-card-rotated');
2526 this.el.removeClass('d-none');
2529 this.collapsableEl.addClass('show');
2530 this.rotated = false;
2531 this.fireEvent('rotate', this, this.rotated);
2534 this.el.addClass('roo-card-rotated');
2535 this.footerEl.addClass('d-none');
2536 this.el.select('.roo-collapsable').removeClass('show');
2538 this.rotated = true;
2539 this.fireEvent('rotate', this, this.rotated);
2543 dropPlaceHolder: function (action, info, data)
2545 if (this.dropEl === false) {
2546 this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2550 this.dropEl.removeClass(['d-none', 'd-block']);
2551 if (action == 'hide') {
2553 this.dropEl.addClass('d-none');
2556 // FIXME - info.card == true!!!
2557 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2559 if (info.card !== true) {
2560 var cardel = info.card.el.dom;
2562 if (info.position == 'above') {
2563 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2564 } else if (cardel.nextSibling) {
2565 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2567 cardel.parentNode.append(this.dropEl.dom);
2570 // card container???
2571 this.bodyEl.dom.append(this.dropEl.dom);
2574 this.dropEl.addClass('d-block roo-card-dropzone');
2576 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2583 setHeaderText: function(html)
2585 this.headerEl.dom.innerHTML = html;
2594 * Card header - holder for the card header elements.
2599 * @class Roo.bootstrap.CardHeader
2600 * @extends Roo.bootstrap.Element
2601 * Bootstrap CardHeader class
2603 * Create a new Card Header - that you can embed children into
2604 * @param {Object} config The config object
2607 Roo.bootstrap.CardHeader = function(config){
2608 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2611 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2614 container_method : 'getCardHeader'
2627 * Card footer - holder for the card footer elements.
2632 * @class Roo.bootstrap.CardFooter
2633 * @extends Roo.bootstrap.Element
2634 * Bootstrap CardFooter class
2636 * Create a new Card Footer - that you can embed children into
2637 * @param {Object} config The config object
2640 Roo.bootstrap.CardFooter = function(config){
2641 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2644 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2647 container_method : 'getCardFooter'
2660 * Card header - holder for the card header elements.
2665 * @class Roo.bootstrap.CardImageTop
2666 * @extends Roo.bootstrap.Element
2667 * Bootstrap CardImageTop class
2669 * Create a new Card Image Top container
2670 * @param {Object} config The config object
2673 Roo.bootstrap.CardImageTop = function(config){
2674 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2677 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2680 container_method : 'getCardImageTop'
2698 * @class Roo.bootstrap.Img
2699 * @extends Roo.bootstrap.Component
2700 * Bootstrap Img class
2701 * @cfg {Boolean} imgResponsive false | true
2702 * @cfg {String} border rounded | circle | thumbnail
2703 * @cfg {String} src image source
2704 * @cfg {String} alt image alternative text
2705 * @cfg {String} href a tag href
2706 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2707 * @cfg {String} xsUrl xs image source
2708 * @cfg {String} smUrl sm image source
2709 * @cfg {String} mdUrl md image source
2710 * @cfg {String} lgUrl lg image source
2713 * Create a new Input
2714 * @param {Object} config The config object
2717 Roo.bootstrap.Img = function(config){
2718 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2724 * The img click event for the img.
2725 * @param {Roo.EventObject} e
2731 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2733 imgResponsive: true,
2743 getAutoCreate : function()
2745 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2746 return this.createSingleImg();
2751 cls: 'roo-image-responsive-group',
2756 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2758 if(!_this[size + 'Url']){
2764 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2765 html: _this.html || cfg.html,
2766 src: _this[size + 'Url']
2769 img.cls += ' roo-image-responsive-' + size;
2771 var s = ['xs', 'sm', 'md', 'lg'];
2773 s.splice(s.indexOf(size), 1);
2775 Roo.each(s, function(ss){
2776 img.cls += ' hidden-' + ss;
2779 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2780 cfg.cls += ' img-' + _this.border;
2784 cfg.alt = _this.alt;
2797 a.target = _this.target;
2801 cfg.cn.push((_this.href) ? a : img);
2808 createSingleImg : function()
2812 cls: (this.imgResponsive) ? 'img-responsive' : '',
2814 src : 'about:blank' // just incase src get's set to undefined?!?
2817 cfg.html = this.html || cfg.html;
2819 cfg.src = this.src || cfg.src;
2821 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2822 cfg.cls += ' img-' + this.border;
2839 a.target = this.target;
2844 return (this.href) ? a : cfg;
2847 initEvents: function()
2850 this.el.on('click', this.onClick, this);
2855 onClick : function(e)
2857 Roo.log('img onclick');
2858 this.fireEvent('click', this, e);
2861 * Sets the url of the image - used to update it
2862 * @param {String} url the url of the image
2865 setSrc : function(url)
2869 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2870 this.el.dom.src = url;
2874 this.el.select('img', true).first().dom.src = url;
2890 * @class Roo.bootstrap.Link
2891 * @extends Roo.bootstrap.Component
2892 * Bootstrap Link Class
2893 * @cfg {String} alt image alternative text
2894 * @cfg {String} href a tag href
2895 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2896 * @cfg {String} html the content of the link.
2897 * @cfg {String} anchor name for the anchor link
2898 * @cfg {String} fa - favicon
2900 * @cfg {Boolean} preventDefault (true | false) default false
2904 * Create a new Input
2905 * @param {Object} config The config object
2908 Roo.bootstrap.Link = function(config){
2909 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2915 * The img click event for the img.
2916 * @param {Roo.EventObject} e
2922 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2926 preventDefault: false,
2932 getAutoCreate : function()
2934 var html = this.html || '';
2936 if (this.fa !== false) {
2937 html = '<i class="fa fa-' + this.fa + '"></i>';
2942 // anchor's do not require html/href...
2943 if (this.anchor === false) {
2945 cfg.href = this.href || '#';
2947 cfg.name = this.anchor;
2948 if (this.html !== false || this.fa !== false) {
2951 if (this.href !== false) {
2952 cfg.href = this.href;
2956 if(this.alt !== false){
2961 if(this.target !== false) {
2962 cfg.target = this.target;
2968 initEvents: function() {
2970 if(!this.href || this.preventDefault){
2971 this.el.on('click', this.onClick, this);
2975 onClick : function(e)
2977 if(this.preventDefault){
2980 //Roo.log('img onclick');
2981 this.fireEvent('click', this, e);
2994 * @class Roo.bootstrap.Header
2995 * @extends Roo.bootstrap.Component
2996 * Bootstrap Header class
2997 * @cfg {String} html content of header
2998 * @cfg {Number} level (1|2|3|4|5|6) default 1
3001 * Create a new Header
3002 * @param {Object} config The config object
3006 Roo.bootstrap.Header = function(config){
3007 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3010 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3018 getAutoCreate : function(){
3023 tag: 'h' + (1 *this.level),
3024 html: this.html || ''
3036 * Ext JS Library 1.1.1
3037 * Copyright(c) 2006-2007, Ext JS, LLC.
3039 * Originally Released Under LGPL - original licence link has changed is not relivant.
3042 * <script type="text/javascript">
3046 * @class Roo.bootstrap.MenuMgr
3047 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3050 Roo.bootstrap.MenuMgr = function(){
3051 var menus, active, groups = {}, attached = false, lastShow = new Date();
3053 // private - called when first menu is created
3056 active = new Roo.util.MixedCollection();
3057 Roo.get(document).addKeyListener(27, function(){
3058 if(active.length > 0){
3066 if(active && active.length > 0){
3067 var c = active.clone();
3077 if(active.length < 1){
3078 Roo.get(document).un("mouseup", onMouseDown);
3086 var last = active.last();
3087 lastShow = new Date();
3090 Roo.get(document).on("mouseup", onMouseDown);
3095 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3096 m.parentMenu.activeChild = m;
3097 }else if(last && last.isVisible()){
3098 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3103 function onBeforeHide(m){
3105 m.activeChild.hide();
3107 if(m.autoHideTimer){
3108 clearTimeout(m.autoHideTimer);
3109 delete m.autoHideTimer;
3114 function onBeforeShow(m){
3115 var pm = m.parentMenu;
3116 if(!pm && !m.allowOtherMenus){
3118 }else if(pm && pm.activeChild && active != m){
3119 pm.activeChild.hide();
3123 // private this should really trigger on mouseup..
3124 function onMouseDown(e){
3125 Roo.log("on Mouse Up");
3127 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3128 Roo.log("MenuManager hideAll");
3137 function onBeforeCheck(mi, state){
3139 var g = groups[mi.group];
3140 for(var i = 0, l = g.length; i < l; i++){
3142 g[i].setChecked(false);
3151 * Hides all menus that are currently visible
3153 hideAll : function(){
3158 register : function(menu){
3162 menus[menu.id] = menu;
3163 menu.on("beforehide", onBeforeHide);
3164 menu.on("hide", onHide);
3165 menu.on("beforeshow", onBeforeShow);
3166 menu.on("show", onShow);
3168 if(g && menu.events["checkchange"]){
3172 groups[g].push(menu);
3173 menu.on("checkchange", onCheck);
3178 * Returns a {@link Roo.menu.Menu} object
3179 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3180 * be used to generate and return a new Menu instance.
3182 get : function(menu){
3183 if(typeof menu == "string"){ // menu id
3185 }else if(menu.events){ // menu instance
3188 /*else if(typeof menu.length == 'number'){ // array of menu items?
3189 return new Roo.bootstrap.Menu({items:menu});
3190 }else{ // otherwise, must be a config
3191 return new Roo.bootstrap.Menu(menu);
3198 unregister : function(menu){
3199 delete menus[menu.id];
3200 menu.un("beforehide", onBeforeHide);
3201 menu.un("hide", onHide);
3202 menu.un("beforeshow", onBeforeShow);
3203 menu.un("show", onShow);
3205 if(g && menu.events["checkchange"]){
3206 groups[g].remove(menu);
3207 menu.un("checkchange", onCheck);
3212 registerCheckable : function(menuItem){
3213 var g = menuItem.group;
3218 groups[g].push(menuItem);
3219 menuItem.on("beforecheckchange", onBeforeCheck);
3224 unregisterCheckable : function(menuItem){
3225 var g = menuItem.group;
3227 groups[g].remove(menuItem);
3228 menuItem.un("beforecheckchange", onBeforeCheck);
3240 * @class Roo.bootstrap.Menu
3241 * @extends Roo.bootstrap.Component
3242 * Bootstrap Menu class - container for MenuItems
3243 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3244 * @cfg {bool} hidden if the menu should be hidden when rendered.
3245 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3246 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3250 * @param {Object} config The config object
3254 Roo.bootstrap.Menu = function(config){
3255 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3256 if (this.registerMenu && this.type != 'treeview') {
3257 Roo.bootstrap.MenuMgr.register(this);
3264 * Fires before this menu is displayed (return false to block)
3265 * @param {Roo.menu.Menu} this
3270 * Fires before this menu is hidden (return false to block)
3271 * @param {Roo.menu.Menu} this
3276 * Fires after this menu is displayed
3277 * @param {Roo.menu.Menu} this
3282 * Fires after this menu is hidden
3283 * @param {Roo.menu.Menu} this
3288 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3289 * @param {Roo.menu.Menu} this
3290 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3291 * @param {Roo.EventObject} e
3296 * Fires when the mouse is hovering over this menu
3297 * @param {Roo.menu.Menu} this
3298 * @param {Roo.EventObject} e
3299 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3304 * Fires when the mouse exits this menu
3305 * @param {Roo.menu.Menu} this
3306 * @param {Roo.EventObject} e
3307 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3312 * Fires when a menu item contained in this menu is clicked
3313 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3314 * @param {Roo.EventObject} e
3318 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3321 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3325 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3328 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3330 registerMenu : true,
3332 menuItems :false, // stores the menu items..
3342 getChildContainer : function() {
3346 getAutoCreate : function(){
3348 //if (['right'].indexOf(this.align)!==-1) {
3349 // cfg.cn[1].cls += ' pull-right'
3355 cls : 'dropdown-menu' ,
3356 style : 'z-index:1000'
3360 if (this.type === 'submenu') {
3361 cfg.cls = 'submenu active';
3363 if (this.type === 'treeview') {
3364 cfg.cls = 'treeview-menu';
3369 initEvents : function() {
3371 // Roo.log("ADD event");
3372 // Roo.log(this.triggerEl.dom);
3374 this.triggerEl.on('click', this.onTriggerClick, this);
3376 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3379 if (this.triggerEl.hasClass('nav-item')) {
3380 // dropdown toggle on the 'a' in BS4?
3381 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3383 this.triggerEl.addClass('dropdown-toggle');
3386 this.el.on('touchstart' , this.onTouch, this);
3388 this.el.on('click' , this.onClick, this);
3390 this.el.on("mouseover", this.onMouseOver, this);
3391 this.el.on("mouseout", this.onMouseOut, this);
3395 findTargetItem : function(e)
3397 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3401 //Roo.log(t); Roo.log(t.id);
3403 //Roo.log(this.menuitems);
3404 return this.menuitems.get(t.id);
3406 //return this.items.get(t.menuItemId);
3412 onTouch : function(e)
3414 Roo.log("menu.onTouch");
3415 //e.stopEvent(); this make the user popdown broken
3419 onClick : function(e)
3421 Roo.log("menu.onClick");
3423 var t = this.findTargetItem(e);
3424 if(!t || t.isContainer){
3429 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3430 if(t == this.activeItem && t.shouldDeactivate(e)){
3431 this.activeItem.deactivate();
3432 delete this.activeItem;
3436 this.setActiveItem(t, true);
3444 Roo.log('pass click event');
3448 this.fireEvent("click", this, t, e);
3452 if(!t.href.length || t.href == '#'){
3453 (function() { _this.hide(); }).defer(100);
3458 onMouseOver : function(e){
3459 var t = this.findTargetItem(e);
3462 // if(t.canActivate && !t.disabled){
3463 // this.setActiveItem(t, true);
3467 this.fireEvent("mouseover", this, e, t);
3469 isVisible : function(){
3470 return !this.hidden;
3472 onMouseOut : function(e){
3473 var t = this.findTargetItem(e);
3476 // if(t == this.activeItem && t.shouldDeactivate(e)){
3477 // this.activeItem.deactivate();
3478 // delete this.activeItem;
3481 this.fireEvent("mouseout", this, e, t);
3486 * Displays this menu relative to another element
3487 * @param {String/HTMLElement/Roo.Element} element The element to align to
3488 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3489 * the element (defaults to this.defaultAlign)
3490 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3492 show : function(el, pos, parentMenu)
3494 if (false === this.fireEvent("beforeshow", this)) {
3495 Roo.log("show canceled");
3498 this.parentMenu = parentMenu;
3503 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3506 * Displays this menu at a specific xy position
3507 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3508 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3510 showAt : function(xy, parentMenu, /* private: */_e){
3511 this.parentMenu = parentMenu;
3516 this.fireEvent("beforeshow", this);
3517 //xy = this.el.adjustForConstraints(xy);
3521 this.hideMenuItems();
3522 this.hidden = false;
3523 this.triggerEl.addClass('open');
3524 this.el.addClass('show');
3526 // reassign x when hitting right
3527 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3528 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3531 // reassign y when hitting bottom
3532 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3533 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3536 // but the list may align on trigger left or trigger top... should it be a properity?
3538 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3543 this.fireEvent("show", this);
3549 this.doFocus.defer(50, this);
3553 doFocus : function(){
3555 this.focusEl.focus();
3560 * Hides this menu and optionally all parent menus
3561 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3563 hide : function(deep)
3565 if (false === this.fireEvent("beforehide", this)) {
3566 Roo.log("hide canceled");
3569 this.hideMenuItems();
3570 if(this.el && this.isVisible()){
3572 if(this.activeItem){
3573 this.activeItem.deactivate();
3574 this.activeItem = null;
3576 this.triggerEl.removeClass('open');;
3577 this.el.removeClass('show');
3579 this.fireEvent("hide", this);
3581 if(deep === true && this.parentMenu){
3582 this.parentMenu.hide(true);
3586 onTriggerClick : function(e)
3588 Roo.log('trigger click');
3590 var target = e.getTarget();
3592 Roo.log(target.nodeName.toLowerCase());
3594 if(target.nodeName.toLowerCase() === 'i'){
3600 onTriggerPress : function(e)
3602 Roo.log('trigger press');
3603 //Roo.log(e.getTarget());
3604 // Roo.log(this.triggerEl.dom);
3606 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3607 var pel = Roo.get(e.getTarget());
3608 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3609 Roo.log('is treeview or dropdown?');
3613 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3617 if (this.isVisible()) {
3622 this.show(this.triggerEl, '?', false);
3625 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3632 hideMenuItems : function()
3634 Roo.log("hide Menu Items");
3639 this.el.select('.open',true).each(function(aa) {
3641 aa.removeClass('open');
3645 addxtypeChild : function (tree, cntr) {
3646 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3648 this.menuitems.add(comp);
3660 this.getEl().dom.innerHTML = '';
3661 this.menuitems.clear();
3675 * @class Roo.bootstrap.MenuItem
3676 * @extends Roo.bootstrap.Component
3677 * Bootstrap MenuItem class
3678 * @cfg {String} html the menu label
3679 * @cfg {String} href the link
3680 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3681 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3682 * @cfg {Boolean} active used on sidebars to highlight active itesm
3683 * @cfg {String} fa favicon to show on left of menu item.
3684 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3688 * Create a new MenuItem
3689 * @param {Object} config The config object
3693 Roo.bootstrap.MenuItem = function(config){
3694 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3699 * The raw click event for the entire grid.
3700 * @param {Roo.bootstrap.MenuItem} this
3701 * @param {Roo.EventObject} e
3707 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3711 preventDefault: false,
3712 isContainer : false,
3716 getAutoCreate : function(){
3718 if(this.isContainer){
3721 cls: 'dropdown-menu-item '
3731 cls : 'dropdown-item',
3736 if (this.fa !== false) {
3739 cls : 'fa fa-' + this.fa
3748 cls: 'dropdown-menu-item',
3751 if (this.parent().type == 'treeview') {
3752 cfg.cls = 'treeview-menu';
3755 cfg.cls += ' active';
3760 anc.href = this.href || cfg.cn[0].href ;
3761 ctag.html = this.html || cfg.cn[0].html ;
3765 initEvents: function()
3767 if (this.parent().type == 'treeview') {
3768 this.el.select('a').on('click', this.onClick, this);
3772 this.menu.parentType = this.xtype;
3773 this.menu.triggerEl = this.el;
3774 this.menu = this.addxtype(Roo.apply({}, this.menu));
3778 onClick : function(e)
3780 Roo.log('item on click ');
3782 if(this.preventDefault){
3785 //this.parent().hideMenuItems();
3787 this.fireEvent('click', this, e);
3806 * @class Roo.bootstrap.MenuSeparator
3807 * @extends Roo.bootstrap.Component
3808 * Bootstrap MenuSeparator class
3811 * Create a new MenuItem
3812 * @param {Object} config The config object
3816 Roo.bootstrap.MenuSeparator = function(config){
3817 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3820 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3822 getAutoCreate : function(){
3841 * @class Roo.bootstrap.Modal
3842 * @extends Roo.bootstrap.Component
3843 * Bootstrap Modal class
3844 * @cfg {String} title Title of dialog
3845 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3846 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3847 * @cfg {Boolean} specificTitle default false
3848 * @cfg {Array} buttons Array of buttons or standard button set..
3849 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3850 * @cfg {Boolean} animate default true
3851 * @cfg {Boolean} allow_close default true
3852 * @cfg {Boolean} fitwindow default false
3853 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3854 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3855 * @cfg {String} size (sm|lg|xl) default empty
3856 * @cfg {Number} max_width set the max width of modal
3857 * @cfg {Boolean} editableTitle can the title be edited
3862 * Create a new Modal Dialog
3863 * @param {Object} config The config object
3866 Roo.bootstrap.Modal = function(config){
3867 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3872 * The raw btnclick event for the button
3873 * @param {Roo.EventObject} e
3878 * Fire when dialog resize
3879 * @param {Roo.bootstrap.Modal} this
3880 * @param {Roo.EventObject} e
3884 * @event titlechanged
3885 * Fire when the editable title has been changed
3886 * @param {Roo.bootstrap.Modal} this
3887 * @param {Roo.EventObject} value
3889 "titlechanged" : true
3892 this.buttons = this.buttons || [];
3895 this.tmpl = Roo.factory(this.tmpl);
3900 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3902 title : 'test dialog',
3912 specificTitle: false,
3914 buttonPosition: 'right',
3936 editableTitle : false,
3938 onRender : function(ct, position)
3940 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3943 var cfg = Roo.apply({}, this.getAutoCreate());
3946 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3948 //if (!cfg.name.length) {
3952 cfg.cls += ' ' + this.cls;
3955 cfg.style = this.style;
3957 this.el = Roo.get(document.body).createChild(cfg, position);
3959 //var type = this.el.dom.type;
3962 if(this.tabIndex !== undefined){
3963 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3966 this.dialogEl = this.el.select('.modal-dialog',true).first();
3967 this.bodyEl = this.el.select('.modal-body',true).first();
3968 this.closeEl = this.el.select('.modal-header .close', true).first();
3969 this.headerEl = this.el.select('.modal-header',true).first();
3970 this.titleEl = this.el.select('.modal-title',true).first();
3971 this.footerEl = this.el.select('.modal-footer',true).first();
3973 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3975 //this.el.addClass("x-dlg-modal");
3977 if (this.buttons.length) {
3978 Roo.each(this.buttons, function(bb) {
3979 var b = Roo.apply({}, bb);
3980 b.xns = b.xns || Roo.bootstrap;
3981 b.xtype = b.xtype || 'Button';
3982 if (typeof(b.listeners) == 'undefined') {
3983 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3986 var btn = Roo.factory(b);
3988 btn.render(this.getButtonContainer());
3992 // render the children.
3995 if(typeof(this.items) != 'undefined'){
3996 var items = this.items;
3999 for(var i =0;i < items.length;i++) {
4000 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4004 this.items = nitems;
4006 // where are these used - they used to be body/close/footer
4010 //this.el.addClass([this.fieldClass, this.cls]);
4014 getAutoCreate : function()
4016 // we will default to modal-body-overflow - might need to remove or make optional later.
4018 cls : 'modal-body enable-modal-body-overflow ',
4019 html : this.html || ''
4024 cls : 'modal-title',
4028 if(this.specificTitle){ // WTF is this?
4033 if (this.allow_close && Roo.bootstrap.version == 3) {
4043 if (this.editableTitle) {
4045 cls: 'form-control roo-editable-title d-none',
4051 if (this.allow_close && Roo.bootstrap.version == 4) {
4061 if(this.size.length){
4062 size = 'modal-' + this.size;
4065 var footer = Roo.bootstrap.version == 3 ?
4067 cls : 'modal-footer',
4071 cls: 'btn-' + this.buttonPosition
4076 { // BS4 uses mr-auto on left buttons....
4077 cls : 'modal-footer'
4088 cls: "modal-dialog " + size,
4091 cls : "modal-content",
4094 cls : 'modal-header',
4109 modal.cls += ' fade';
4115 getChildContainer : function() {
4120 getButtonContainer : function() {
4122 return Roo.bootstrap.version == 4 ?
4123 this.el.select('.modal-footer',true).first()
4124 : this.el.select('.modal-footer div',true).first();
4127 initEvents : function()
4129 if (this.allow_close) {
4130 this.closeEl.on('click', this.hide, this);
4132 Roo.EventManager.onWindowResize(this.resize, this, true);
4133 if (this.editableTitle) {
4134 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4135 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4136 this.headerEditEl.on('keyup', function(e) {
4137 if(e.isNavKeyPress()){
4138 this.toggleHeaderInput(false)
4141 this.headerEditEl.on('blur', function(e) {
4142 this.toggleHeaderInput(false)
4151 this.maskEl.setSize(
4152 Roo.lib.Dom.getViewWidth(true),
4153 Roo.lib.Dom.getViewHeight(true)
4156 if (this.fitwindow) {
4160 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4161 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4166 if(this.max_width !== 0) {
4168 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4171 this.setSize(w, this.height);
4175 if(this.max_height) {
4176 this.setSize(w,Math.min(
4178 Roo.lib.Dom.getViewportHeight(true) - 60
4184 if(!this.fit_content) {
4185 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4189 this.setSize(w, Math.min(
4191 this.headerEl.getHeight() +
4192 this.footerEl.getHeight() +
4193 this.getChildHeight(this.bodyEl.dom.childNodes),
4194 Roo.lib.Dom.getViewportHeight(true) - 60)
4200 setSize : function(w,h)
4211 if (!this.rendered) {
4215 //this.el.setStyle('display', 'block');
4216 this.el.removeClass('hideing');
4217 this.el.dom.style.display='block';
4219 Roo.get(document.body).addClass('modal-open');
4221 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4224 this.el.addClass('show');
4225 this.el.addClass('in');
4228 this.el.addClass('show');
4229 this.el.addClass('in');
4232 // not sure how we can show data in here..
4234 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4237 Roo.get(document.body).addClass("x-body-masked");
4239 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4240 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4241 this.maskEl.dom.style.display = 'block';
4242 this.maskEl.addClass('show');
4247 this.fireEvent('show', this);
4249 // set zindex here - otherwise it appears to be ignored...
4250 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4253 this.items.forEach( function(e) {
4254 e.layout ? e.layout() : false;
4262 if(this.fireEvent("beforehide", this) !== false){
4264 this.maskEl.removeClass('show');
4266 this.maskEl.dom.style.display = '';
4267 Roo.get(document.body).removeClass("x-body-masked");
4268 this.el.removeClass('in');
4269 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4271 if(this.animate){ // why
4272 this.el.addClass('hideing');
4273 this.el.removeClass('show');
4275 if (!this.el.hasClass('hideing')) {
4276 return; // it's been shown again...
4279 this.el.dom.style.display='';
4281 Roo.get(document.body).removeClass('modal-open');
4282 this.el.removeClass('hideing');
4286 this.el.removeClass('show');
4287 this.el.dom.style.display='';
4288 Roo.get(document.body).removeClass('modal-open');
4291 this.fireEvent('hide', this);
4294 isVisible : function()
4297 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4301 addButton : function(str, cb)
4305 var b = Roo.apply({}, { html : str } );
4306 b.xns = b.xns || Roo.bootstrap;
4307 b.xtype = b.xtype || 'Button';
4308 if (typeof(b.listeners) == 'undefined') {
4309 b.listeners = { click : cb.createDelegate(this) };
4312 var btn = Roo.factory(b);
4314 btn.render(this.getButtonContainer());
4320 setDefaultButton : function(btn)
4322 //this.el.select('.modal-footer').()
4325 resizeTo: function(w,h)
4327 this.dialogEl.setWidth(w);
4329 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4331 this.bodyEl.setHeight(h - diff);
4333 this.fireEvent('resize', this);
4336 setContentSize : function(w, h)
4340 onButtonClick: function(btn,e)
4343 this.fireEvent('btnclick', btn.name, e);
4346 * Set the title of the Dialog
4347 * @param {String} str new Title
4349 setTitle: function(str) {
4350 this.titleEl.dom.innerHTML = str;
4354 * Set the body of the Dialog
4355 * @param {String} str new Title
4357 setBody: function(str) {
4358 this.bodyEl.dom.innerHTML = str;
4361 * Set the body of the Dialog using the template
4362 * @param {Obj} data - apply this data to the template and replace the body contents.
4364 applyBody: function(obj)
4367 Roo.log("Error - using apply Body without a template");
4370 this.tmpl.overwrite(this.bodyEl, obj);
4373 getChildHeight : function(child_nodes)
4377 child_nodes.length == 0
4382 var child_height = 0;
4384 for(var i = 0; i < child_nodes.length; i++) {
4387 * for modal with tabs...
4388 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4390 var layout_childs = child_nodes[i].childNodes;
4392 for(var j = 0; j < layout_childs.length; j++) {
4394 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4396 var layout_body_childs = layout_childs[j].childNodes;
4398 for(var k = 0; k < layout_body_childs.length; k++) {
4400 if(layout_body_childs[k].classList.contains('navbar')) {
4401 child_height += layout_body_childs[k].offsetHeight;
4405 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4407 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4409 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4411 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4412 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4427 child_height += child_nodes[i].offsetHeight;
4428 // Roo.log(child_nodes[i].offsetHeight);
4431 return child_height;
4433 toggleHeaderInput : function(is_edit)
4436 if (is_edit && this.is_header_editing) {
4437 return; // already editing..
4441 this.headerEditEl.dom.value = this.title;
4442 this.headerEditEl.removeClass('d-none');
4443 this.headerEditEl.dom.focus();
4444 this.titleEl.addClass('d-none');
4446 this.is_header_editing = true;
4449 // flip back to not editing.
4450 this.title = this.headerEditEl.dom.value;
4451 this.headerEditEl.addClass('d-none');
4452 this.titleEl.removeClass('d-none');
4453 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4454 this.is_header_editing = false;
4455 this.fireEvent('titlechanged', this, this.title);
4464 Roo.apply(Roo.bootstrap.Modal, {
4466 * Button config that displays a single OK button
4475 * Button config that displays Yes and No buttons
4491 * Button config that displays OK and Cancel buttons
4506 * Button config that displays Yes, No and Cancel buttons
4531 * messagebox - can be used as a replace
4535 * @class Roo.MessageBox
4536 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4540 Roo.Msg.alert('Status', 'Changes saved successfully.');
4542 // Prompt for user data:
4543 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4545 // process text value...
4549 // Show a dialog using config options:
4551 title:'Save Changes?',
4552 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4553 buttons: Roo.Msg.YESNOCANCEL,
4560 Roo.bootstrap.MessageBox = function(){
4561 var dlg, opt, mask, waitTimer;
4562 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4563 var buttons, activeTextEl, bwidth;
4567 var handleButton = function(button){
4569 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4573 var handleHide = function(){
4575 dlg.el.removeClass(opt.cls);
4578 // Roo.TaskMgr.stop(waitTimer);
4579 // waitTimer = null;
4584 var updateButtons = function(b){
4587 buttons["ok"].hide();
4588 buttons["cancel"].hide();
4589 buttons["yes"].hide();
4590 buttons["no"].hide();
4591 dlg.footerEl.hide();
4595 dlg.footerEl.show();
4596 for(var k in buttons){
4597 if(typeof buttons[k] != "function"){
4600 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4601 width += buttons[k].el.getWidth()+15;
4611 var handleEsc = function(d, k, e){
4612 if(opt && opt.closable !== false){
4622 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4623 * @return {Roo.BasicDialog} The BasicDialog element
4625 getDialog : function(){
4627 dlg = new Roo.bootstrap.Modal( {
4630 //constraintoviewport:false,
4632 //collapsible : false,
4637 //buttonAlign:"center",
4638 closeClick : function(){
4639 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4642 handleButton("cancel");
4647 dlg.on("hide", handleHide);
4649 //dlg.addKeyListener(27, handleEsc);
4651 this.buttons = buttons;
4652 var bt = this.buttonText;
4653 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4654 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4655 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4656 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4658 bodyEl = dlg.bodyEl.createChild({
4660 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4661 '<textarea class="roo-mb-textarea"></textarea>' +
4662 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4664 msgEl = bodyEl.dom.firstChild;
4665 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4666 textboxEl.enableDisplayMode();
4667 textboxEl.addKeyListener([10,13], function(){
4668 if(dlg.isVisible() && opt && opt.buttons){
4671 }else if(opt.buttons.yes){
4672 handleButton("yes");
4676 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4677 textareaEl.enableDisplayMode();
4678 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4679 progressEl.enableDisplayMode();
4681 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4682 var pf = progressEl.dom.firstChild;
4684 pp = Roo.get(pf.firstChild);
4685 pp.setHeight(pf.offsetHeight);
4693 * Updates the message box body text
4694 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4695 * the XHTML-compliant non-breaking space character '&#160;')
4696 * @return {Roo.MessageBox} This message box
4698 updateText : function(text)
4700 if(!dlg.isVisible() && !opt.width){
4701 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4702 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4704 msgEl.innerHTML = text || ' ';
4706 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4707 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4709 Math.min(opt.width || cw , this.maxWidth),
4710 Math.max(opt.minWidth || this.minWidth, bwidth)
4713 activeTextEl.setWidth(w);
4715 if(dlg.isVisible()){
4716 dlg.fixedcenter = false;
4718 // to big, make it scroll. = But as usual stupid IE does not support
4721 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4722 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4723 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4725 bodyEl.dom.style.height = '';
4726 bodyEl.dom.style.overflowY = '';
4729 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4731 bodyEl.dom.style.overflowX = '';
4734 dlg.setContentSize(w, bodyEl.getHeight());
4735 if(dlg.isVisible()){
4736 dlg.fixedcenter = true;
4742 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4743 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4744 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4745 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4746 * @return {Roo.MessageBox} This message box
4748 updateProgress : function(value, text){
4750 this.updateText(text);
4753 if (pp) { // weird bug on my firefox - for some reason this is not defined
4754 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4755 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4761 * Returns true if the message box is currently displayed
4762 * @return {Boolean} True if the message box is visible, else false
4764 isVisible : function(){
4765 return dlg && dlg.isVisible();
4769 * Hides the message box if it is displayed
4772 if(this.isVisible()){
4778 * Displays a new message box, or reinitializes an existing message box, based on the config options
4779 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4780 * The following config object properties are supported:
4782 Property Type Description
4783 ---------- --------------- ------------------------------------------------------------------------------------
4784 animEl String/Element An id or Element from which the message box should animate as it opens and
4785 closes (defaults to undefined)
4786 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4787 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4788 closable Boolean False to hide the top-right close button (defaults to true). Note that
4789 progress and wait dialogs will ignore this property and always hide the
4790 close button as they can only be closed programmatically.
4791 cls String A custom CSS class to apply to the message box element
4792 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4793 displayed (defaults to 75)
4794 fn Function A callback function to execute after closing the dialog. The arguments to the
4795 function will be btn (the name of the button that was clicked, if applicable,
4796 e.g. "ok"), and text (the value of the active text field, if applicable).
4797 Progress and wait dialogs will ignore this option since they do not respond to
4798 user actions and can only be closed programmatically, so any required function
4799 should be called by the same code after it closes the dialog.
4800 icon String A CSS class that provides a background image to be used as an icon for
4801 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4802 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4803 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4804 modal Boolean False to allow user interaction with the page while the message box is
4805 displayed (defaults to true)
4806 msg String A string that will replace the existing message box body text (defaults
4807 to the XHTML-compliant non-breaking space character ' ')
4808 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4809 progress Boolean True to display a progress bar (defaults to false)
4810 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4811 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4812 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4813 title String The title text
4814 value String The string value to set into the active textbox element if displayed
4815 wait Boolean True to display a progress bar (defaults to false)
4816 width Number The width of the dialog in pixels
4823 msg: 'Please enter your address:',
4825 buttons: Roo.MessageBox.OKCANCEL,
4828 animEl: 'addAddressBtn'
4831 * @param {Object} config Configuration options
4832 * @return {Roo.MessageBox} This message box
4834 show : function(options)
4837 // this causes nightmares if you show one dialog after another
4838 // especially on callbacks..
4840 if(this.isVisible()){
4843 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4844 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4845 Roo.log("New Dialog Message:" + options.msg )
4846 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4847 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4850 var d = this.getDialog();
4852 d.setTitle(opt.title || " ");
4853 d.closeEl.setDisplayed(opt.closable !== false);
4854 activeTextEl = textboxEl;
4855 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4860 textareaEl.setHeight(typeof opt.multiline == "number" ?
4861 opt.multiline : this.defaultTextHeight);
4862 activeTextEl = textareaEl;
4871 progressEl.setDisplayed(opt.progress === true);
4873 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4875 this.updateProgress(0);
4876 activeTextEl.dom.value = opt.value || "";
4878 dlg.setDefaultButton(activeTextEl);
4880 var bs = opt.buttons;
4884 }else if(bs && bs.yes){
4885 db = buttons["yes"];
4887 dlg.setDefaultButton(db);
4889 bwidth = updateButtons(opt.buttons);
4890 this.updateText(opt.msg);
4892 d.el.addClass(opt.cls);
4894 d.proxyDrag = opt.proxyDrag === true;
4895 d.modal = opt.modal !== false;
4896 d.mask = opt.modal !== false ? mask : false;
4898 // force it to the end of the z-index stack so it gets a cursor in FF
4899 document.body.appendChild(dlg.el.dom);
4900 d.animateTarget = null;
4901 d.show(options.animEl);
4907 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4908 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4909 * and closing the message box when the process is complete.
4910 * @param {String} title The title bar text
4911 * @param {String} msg The message box body text
4912 * @return {Roo.MessageBox} This message box
4914 progress : function(title, msg){
4921 minWidth: this.minProgressWidth,
4928 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4929 * If a callback function is passed it will be called after the user clicks the button, and the
4930 * id of the button that was clicked will be passed as the only parameter to the callback
4931 * (could also be the top-right close button).
4932 * @param {String} title The title bar text
4933 * @param {String} msg The message box body text
4934 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4935 * @param {Object} scope (optional) The scope of the callback function
4936 * @return {Roo.MessageBox} This message box
4938 alert : function(title, msg, fn, scope)
4953 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4954 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4955 * You are responsible for closing the message box when the process is complete.
4956 * @param {String} msg The message box body text
4957 * @param {String} title (optional) The title bar text
4958 * @return {Roo.MessageBox} This message box
4960 wait : function(msg, title){
4971 waitTimer = Roo.TaskMgr.start({
4973 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4981 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4982 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4983 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4984 * @param {String} title The title bar text
4985 * @param {String} msg The message box body text
4986 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4987 * @param {Object} scope (optional) The scope of the callback function
4988 * @return {Roo.MessageBox} This message box
4990 confirm : function(title, msg, fn, scope){
4994 buttons: this.YESNO,
5003 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5004 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5005 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5006 * (could also be the top-right close button) and the text that was entered will be passed as the two
5007 * parameters to the callback.
5008 * @param {String} title The title bar text
5009 * @param {String} msg The message box body text
5010 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5011 * @param {Object} scope (optional) The scope of the callback function
5012 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5013 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5014 * @return {Roo.MessageBox} This message box
5016 prompt : function(title, msg, fn, scope, multiline){
5020 buttons: this.OKCANCEL,
5025 multiline: multiline,
5032 * Button config that displays a single OK button
5037 * Button config that displays Yes and No buttons
5040 YESNO : {yes:true, no:true},
5042 * Button config that displays OK and Cancel buttons
5045 OKCANCEL : {ok:true, cancel:true},
5047 * Button config that displays Yes, No and Cancel buttons
5050 YESNOCANCEL : {yes:true, no:true, cancel:true},
5053 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5056 defaultTextHeight : 75,
5058 * The maximum width in pixels of the message box (defaults to 600)
5063 * The minimum width in pixels of the message box (defaults to 100)
5068 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5069 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5072 minProgressWidth : 250,
5074 * An object containing the default button text strings that can be overriden for localized language support.
5075 * Supported properties are: ok, cancel, yes and no.
5076 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5089 * Shorthand for {@link Roo.MessageBox}
5091 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5092 Roo.Msg = Roo.Msg || Roo.MessageBox;
5101 * @class Roo.bootstrap.Navbar
5102 * @extends Roo.bootstrap.Component
5103 * Bootstrap Navbar class
5106 * Create a new Navbar
5107 * @param {Object} config The config object
5111 Roo.bootstrap.Navbar = function(config){
5112 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5116 * @event beforetoggle
5117 * Fire before toggle the menu
5118 * @param {Roo.EventObject} e
5120 "beforetoggle" : true
5124 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5133 getAutoCreate : function(){
5136 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5140 initEvents :function ()
5142 //Roo.log(this.el.select('.navbar-toggle',true));
5143 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5150 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5152 var size = this.el.getSize();
5153 this.maskEl.setSize(size.width, size.height);
5154 this.maskEl.enableDisplayMode("block");
5163 getChildContainer : function()
5165 if (this.el && this.el.select('.collapse').getCount()) {
5166 return this.el.select('.collapse',true).first();
5181 onToggle : function()
5184 if(this.fireEvent('beforetoggle', this) === false){
5187 var ce = this.el.select('.navbar-collapse',true).first();
5189 if (!ce.hasClass('show')) {
5199 * Expand the navbar pulldown
5201 expand : function ()
5204 var ce = this.el.select('.navbar-collapse',true).first();
5205 if (ce.hasClass('collapsing')) {
5208 ce.dom.style.height = '';
5210 ce.addClass('in'); // old...
5211 ce.removeClass('collapse');
5212 ce.addClass('show');
5213 var h = ce.getHeight();
5215 ce.removeClass('show');
5216 // at this point we should be able to see it..
5217 ce.addClass('collapsing');
5219 ce.setHeight(0); // resize it ...
5220 ce.on('transitionend', function() {
5221 //Roo.log('done transition');
5222 ce.removeClass('collapsing');
5223 ce.addClass('show');
5224 ce.removeClass('collapse');
5226 ce.dom.style.height = '';
5227 }, this, { single: true} );
5229 ce.dom.scrollTop = 0;
5232 * Collapse the navbar pulldown
5234 collapse : function()
5236 var ce = this.el.select('.navbar-collapse',true).first();
5238 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5239 // it's collapsed or collapsing..
5242 ce.removeClass('in'); // old...
5243 ce.setHeight(ce.getHeight());
5244 ce.removeClass('show');
5245 ce.addClass('collapsing');
5247 ce.on('transitionend', function() {
5248 ce.dom.style.height = '';
5249 ce.removeClass('collapsing');
5250 ce.addClass('collapse');
5251 }, this, { single: true} );
5271 * @class Roo.bootstrap.NavSimplebar
5272 * @extends Roo.bootstrap.Navbar
5273 * Bootstrap Sidebar class
5275 * @cfg {Boolean} inverse is inverted color
5277 * @cfg {String} type (nav | pills | tabs)
5278 * @cfg {Boolean} arrangement stacked | justified
5279 * @cfg {String} align (left | right) alignment
5281 * @cfg {Boolean} main (true|false) main nav bar? default false
5282 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5284 * @cfg {String} tag (header|footer|nav|div) default is nav
5286 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5290 * Create a new Sidebar
5291 * @param {Object} config The config object
5295 Roo.bootstrap.NavSimplebar = function(config){
5296 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5299 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5315 getAutoCreate : function(){
5319 tag : this.tag || 'div',
5320 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5322 if (['light','white'].indexOf(this.weight) > -1) {
5323 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5325 cfg.cls += ' bg-' + this.weight;
5328 cfg.cls += ' navbar-inverse';
5332 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5334 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5343 cls: 'nav nav-' + this.xtype,
5349 this.type = this.type || 'nav';
5350 if (['tabs','pills'].indexOf(this.type) != -1) {
5351 cfg.cn[0].cls += ' nav-' + this.type
5355 if (this.type!=='nav') {
5356 Roo.log('nav type must be nav/tabs/pills')
5358 cfg.cn[0].cls += ' navbar-nav'
5364 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5365 cfg.cn[0].cls += ' nav-' + this.arrangement;
5369 if (this.align === 'right') {
5370 cfg.cn[0].cls += ' navbar-right';
5395 * navbar-expand-md fixed-top
5399 * @class Roo.bootstrap.NavHeaderbar
5400 * @extends Roo.bootstrap.NavSimplebar
5401 * Bootstrap Sidebar class
5403 * @cfg {String} brand what is brand
5404 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5405 * @cfg {String} brand_href href of the brand
5406 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5407 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5408 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5409 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5412 * Create a new Sidebar
5413 * @param {Object} config The config object
5417 Roo.bootstrap.NavHeaderbar = function(config){
5418 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5422 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5429 desktopCenter : false,
5432 getAutoCreate : function(){
5435 tag: this.nav || 'nav',
5436 cls: 'navbar navbar-expand-md',
5442 if (this.desktopCenter) {
5443 cn.push({cls : 'container', cn : []});
5451 cls: 'navbar-toggle navbar-toggler',
5452 'data-toggle': 'collapse',
5457 html: 'Toggle navigation'
5461 cls: 'icon-bar navbar-toggler-icon'
5474 cn.push( Roo.bootstrap.version == 4 ? btn : {
5476 cls: 'navbar-header',
5485 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5489 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5491 if (['light','white'].indexOf(this.weight) > -1) {
5492 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5494 cfg.cls += ' bg-' + this.weight;
5497 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5498 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5500 // tag can override this..
5502 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5505 if (this.brand !== '') {
5506 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5507 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5509 href: this.brand_href ? this.brand_href : '#',
5510 cls: 'navbar-brand',
5518 cfg.cls += ' main-nav';
5526 getHeaderChildContainer : function()
5528 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5529 return this.el.select('.navbar-header',true).first();
5532 return this.getChildContainer();
5535 getChildContainer : function()
5538 return this.el.select('.roo-navbar-collapse',true).first();
5543 initEvents : function()
5545 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5547 if (this.autohide) {
5552 Roo.get(document).on('scroll',function(e) {
5553 var ns = Roo.get(document).getScroll().top;
5554 var os = prevScroll;
5558 ft.removeClass('slideDown');
5559 ft.addClass('slideUp');
5562 ft.removeClass('slideUp');
5563 ft.addClass('slideDown');
5584 * @class Roo.bootstrap.NavSidebar
5585 * @extends Roo.bootstrap.Navbar
5586 * Bootstrap Sidebar class
5589 * Create a new Sidebar
5590 * @param {Object} config The config object
5594 Roo.bootstrap.NavSidebar = function(config){
5595 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5598 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5600 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5602 getAutoCreate : function(){
5607 cls: 'sidebar sidebar-nav'
5629 * @class Roo.bootstrap.NavGroup
5630 * @extends Roo.bootstrap.Component
5631 * Bootstrap NavGroup class
5632 * @cfg {String} align (left|right)
5633 * @cfg {Boolean} inverse
5634 * @cfg {String} type (nav|pills|tab) default nav
5635 * @cfg {String} navId - reference Id for navbar.
5639 * Create a new nav group
5640 * @param {Object} config The config object
5643 Roo.bootstrap.NavGroup = function(config){
5644 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5647 Roo.bootstrap.NavGroup.register(this);
5651 * Fires when the active item changes
5652 * @param {Roo.bootstrap.NavGroup} this
5653 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5654 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5661 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5672 getAutoCreate : function()
5674 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5680 if (Roo.bootstrap.version == 4) {
5681 if (['tabs','pills'].indexOf(this.type) != -1) {
5682 cfg.cls += ' nav-' + this.type;
5684 // trying to remove so header bar can right align top?
5685 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5686 // do not use on header bar...
5687 cfg.cls += ' navbar-nav';
5692 if (['tabs','pills'].indexOf(this.type) != -1) {
5693 cfg.cls += ' nav-' + this.type
5695 if (this.type !== 'nav') {
5696 Roo.log('nav type must be nav/tabs/pills')
5698 cfg.cls += ' navbar-nav'
5702 if (this.parent() && this.parent().sidebar) {
5705 cls: 'dashboard-menu sidebar-menu'
5711 if (this.form === true) {
5714 cls: 'navbar-form form-inline'
5716 //nav navbar-right ml-md-auto
5717 if (this.align === 'right') {
5718 cfg.cls += ' navbar-right ml-md-auto';
5720 cfg.cls += ' navbar-left';
5724 if (this.align === 'right') {
5725 cfg.cls += ' navbar-right ml-md-auto';
5727 cfg.cls += ' mr-auto';
5731 cfg.cls += ' navbar-inverse';
5739 * sets the active Navigation item
5740 * @param {Roo.bootstrap.NavItem} the new current navitem
5742 setActiveItem : function(item)
5745 Roo.each(this.navItems, function(v){
5750 v.setActive(false, true);
5757 item.setActive(true, true);
5758 this.fireEvent('changed', this, item, prev);
5763 * gets the active Navigation item
5764 * @return {Roo.bootstrap.NavItem} the current navitem
5766 getActive : function()
5770 Roo.each(this.navItems, function(v){
5781 indexOfNav : function()
5785 Roo.each(this.navItems, function(v,i){
5796 * adds a Navigation item
5797 * @param {Roo.bootstrap.NavItem} the navitem to add
5799 addItem : function(cfg)
5801 if (this.form && Roo.bootstrap.version == 4) {
5804 var cn = new Roo.bootstrap.NavItem(cfg);
5806 cn.parentId = this.id;
5807 cn.onRender(this.el, null);
5811 * register a Navigation item
5812 * @param {Roo.bootstrap.NavItem} the navitem to add
5814 register : function(item)
5816 this.navItems.push( item);
5817 item.navId = this.navId;
5822 * clear all the Navigation item
5825 clearAll : function()
5828 this.el.dom.innerHTML = '';
5831 getNavItem: function(tabId)
5834 Roo.each(this.navItems, function(e) {
5835 if (e.tabId == tabId) {
5845 setActiveNext : function()
5847 var i = this.indexOfNav(this.getActive());
5848 if (i > this.navItems.length) {
5851 this.setActiveItem(this.navItems[i+1]);
5853 setActivePrev : function()
5855 var i = this.indexOfNav(this.getActive());
5859 this.setActiveItem(this.navItems[i-1]);
5861 clearWasActive : function(except) {
5862 Roo.each(this.navItems, function(e) {
5863 if (e.tabId != except.tabId && e.was_active) {
5864 e.was_active = false;
5871 getWasActive : function ()
5874 Roo.each(this.navItems, function(e) {
5889 Roo.apply(Roo.bootstrap.NavGroup, {
5893 * register a Navigation Group
5894 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5896 register : function(navgrp)
5898 this.groups[navgrp.navId] = navgrp;
5902 * fetch a Navigation Group based on the navigation ID
5903 * @param {string} the navgroup to add
5904 * @returns {Roo.bootstrap.NavGroup} the navgroup
5906 get: function(navId) {
5907 if (typeof(this.groups[navId]) == 'undefined') {
5909 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5911 return this.groups[navId] ;
5926 * @class Roo.bootstrap.NavItem
5927 * @extends Roo.bootstrap.Component
5928 * Bootstrap Navbar.NavItem class
5929 * @cfg {String} href link to
5930 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5932 * @cfg {String} html content of button
5933 * @cfg {String} badge text inside badge
5934 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5935 * @cfg {String} glyphicon DEPRICATED - use fa
5936 * @cfg {String} icon DEPRICATED - use fa
5937 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5938 * @cfg {Boolean} active Is item active
5939 * @cfg {Boolean} disabled Is item disabled
5941 * @cfg {Boolean} preventDefault (true | false) default false
5942 * @cfg {String} tabId the tab that this item activates.
5943 * @cfg {String} tagtype (a|span) render as a href or span?
5944 * @cfg {Boolean} animateRef (true|false) link to element default false
5947 * Create a new Navbar Item
5948 * @param {Object} config The config object
5950 Roo.bootstrap.NavItem = function(config){
5951 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5956 * The raw click event for the entire grid.
5957 * @param {Roo.EventObject} e
5962 * Fires when the active item active state changes
5963 * @param {Roo.bootstrap.NavItem} this
5964 * @param {boolean} state the new state
5970 * Fires when scroll to element
5971 * @param {Roo.bootstrap.NavItem} this
5972 * @param {Object} options
5973 * @param {Roo.EventObject} e
5981 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5990 preventDefault : false,
5998 button_outline : false,
6002 getAutoCreate : function(){
6010 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6012 if (this.disabled) {
6013 cfg.cls += ' disabled';
6017 if (this.button_weight.length) {
6018 cfg.tag = this.href ? 'a' : 'button';
6019 cfg.html = this.html || '';
6020 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6022 cfg.href = this.href;
6025 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6028 // menu .. should add dropdown-menu class - so no need for carat..
6030 if (this.badge !== '') {
6032 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6037 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6041 href : this.href || "#",
6042 html: this.html || ''
6045 if (this.tagtype == 'a') {
6046 cfg.cn[0].cls = 'nav-link';
6049 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6052 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6054 if(this.glyphicon) {
6055 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6060 cfg.cn[0].html += " <span class='caret'></span>";
6064 if (this.badge !== '') {
6066 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6074 onRender : function(ct, position)
6076 // Roo.log("Call onRender: " + this.xtype);
6077 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6081 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6082 this.navLink = this.el.select('.nav-link',true).first();
6087 initEvents: function()
6089 if (typeof (this.menu) != 'undefined') {
6090 this.menu.parentType = this.xtype;
6091 this.menu.triggerEl = this.el;
6092 this.menu = this.addxtype(Roo.apply({}, this.menu));
6095 this.el.select('a',true).on('click', this.onClick, this);
6097 if(this.tagtype == 'span'){
6098 this.el.select('span',true).on('click', this.onClick, this);
6101 // at this point parent should be available..
6102 this.parent().register(this);
6105 onClick : function(e)
6107 if (e.getTarget('.dropdown-menu-item')) {
6108 // did you click on a menu itemm.... - then don't trigger onclick..
6113 this.preventDefault ||
6116 Roo.log("NavItem - prevent Default?");
6120 if (this.disabled) {
6124 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6125 if (tg && tg.transition) {
6126 Roo.log("waiting for the transitionend");
6132 //Roo.log("fire event clicked");
6133 if(this.fireEvent('click', this, e) === false){
6137 if(this.tagtype == 'span'){
6141 //Roo.log(this.href);
6142 var ael = this.el.select('a',true).first();
6145 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6146 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6147 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6148 return; // ignore... - it's a 'hash' to another page.
6150 Roo.log("NavItem - prevent Default?");
6152 this.scrollToElement(e);
6156 var p = this.parent();
6158 if (['tabs','pills'].indexOf(p.type)!==-1) {
6159 if (typeof(p.setActiveItem) !== 'undefined') {
6160 p.setActiveItem(this);
6164 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6165 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6166 // remove the collapsed menu expand...
6167 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6171 isActive: function () {
6174 setActive : function(state, fire, is_was_active)
6176 if (this.active && !state && this.navId) {
6177 this.was_active = true;
6178 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6180 nv.clearWasActive(this);
6184 this.active = state;
6187 this.el.removeClass('active');
6188 this.navLink ? this.navLink.removeClass('active') : false;
6189 } else if (!this.el.hasClass('active')) {
6191 this.el.addClass('active');
6192 if (Roo.bootstrap.version == 4 && this.navLink ) {
6193 this.navLink.addClass('active');
6198 this.fireEvent('changed', this, state);
6201 // show a panel if it's registered and related..
6203 if (!this.navId || !this.tabId || !state || is_was_active) {
6207 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6211 var pan = tg.getPanelByName(this.tabId);
6215 // if we can not flip to new panel - go back to old nav highlight..
6216 if (false == tg.showPanel(pan)) {
6217 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6219 var onav = nv.getWasActive();
6221 onav.setActive(true, false, true);
6230 // this should not be here...
6231 setDisabled : function(state)
6233 this.disabled = state;
6235 this.el.removeClass('disabled');
6236 } else if (!this.el.hasClass('disabled')) {
6237 this.el.addClass('disabled');
6243 * Fetch the element to display the tooltip on.
6244 * @return {Roo.Element} defaults to this.el
6246 tooltipEl : function()
6248 return this.el.select('' + this.tagtype + '', true).first();
6251 scrollToElement : function(e)
6253 var c = document.body;
6256 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6258 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6259 c = document.documentElement;
6262 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6268 var o = target.calcOffsetsTo(c);
6275 this.fireEvent('scrollto', this, options, e);
6277 Roo.get(c).scrollTo('top', options.value, true);
6290 * <span> icon </span>
6291 * <span> text </span>
6292 * <span>badge </span>
6296 * @class Roo.bootstrap.NavSidebarItem
6297 * @extends Roo.bootstrap.NavItem
6298 * Bootstrap Navbar.NavSidebarItem class
6299 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6300 * {Boolean} open is the menu open
6301 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6302 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6303 * {String} buttonSize (sm|md|lg)the extra classes for the button
6304 * {Boolean} showArrow show arrow next to the text (default true)
6306 * Create a new Navbar Button
6307 * @param {Object} config The config object
6309 Roo.bootstrap.NavSidebarItem = function(config){
6310 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6315 * The raw click event for the entire grid.
6316 * @param {Roo.EventObject} e
6321 * Fires when the active item active state changes
6322 * @param {Roo.bootstrap.NavSidebarItem} this
6323 * @param {boolean} state the new state
6331 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6333 badgeWeight : 'default',
6339 buttonWeight : 'default',
6345 getAutoCreate : function(){
6350 href : this.href || '#',
6356 if(this.buttonView){
6359 href : this.href || '#',
6360 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6373 cfg.cls += ' active';
6376 if (this.disabled) {
6377 cfg.cls += ' disabled';
6380 cfg.cls += ' open x-open';
6383 if (this.glyphicon || this.icon) {
6384 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6385 a.cn.push({ tag : 'i', cls : c }) ;
6388 if(!this.buttonView){
6391 html : this.html || ''
6398 if (this.badge !== '') {
6399 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6405 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6408 a.cls += ' dropdown-toggle treeview' ;
6414 initEvents : function()
6416 if (typeof (this.menu) != 'undefined') {
6417 this.menu.parentType = this.xtype;
6418 this.menu.triggerEl = this.el;
6419 this.menu = this.addxtype(Roo.apply({}, this.menu));
6422 this.el.on('click', this.onClick, this);
6424 if(this.badge !== ''){
6425 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6430 onClick : function(e)
6437 if(this.preventDefault){
6441 this.fireEvent('click', this, e);
6444 disable : function()
6446 this.setDisabled(true);
6451 this.setDisabled(false);
6454 setDisabled : function(state)
6456 if(this.disabled == state){
6460 this.disabled = state;
6463 this.el.addClass('disabled');
6467 this.el.removeClass('disabled');
6472 setActive : function(state)
6474 if(this.active == state){
6478 this.active = state;
6481 this.el.addClass('active');
6485 this.el.removeClass('active');
6490 isActive: function ()
6495 setBadge : function(str)
6501 this.badgeEl.dom.innerHTML = str;
6518 * @class Roo.bootstrap.Row
6519 * @extends Roo.bootstrap.Component
6520 * Bootstrap Row class (contains columns...)
6524 * @param {Object} config The config object
6527 Roo.bootstrap.Row = function(config){
6528 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6531 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6533 getAutoCreate : function(){
6552 * @class Roo.bootstrap.Pagination
6553 * @extends Roo.bootstrap.Component
6554 * Bootstrap Pagination class
6555 * @cfg {String} size xs | sm | md | lg
6556 * @cfg {Boolean} inverse false | true
6559 * Create a new Pagination
6560 * @param {Object} config The config object
6563 Roo.bootstrap.Pagination = function(config){
6564 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6573 getAutoCreate : function(){
6579 cfg.cls += ' inverse';
6585 cfg.cls += " " + this.cls;
6603 * @class Roo.bootstrap.PaginationItem
6604 * @extends Roo.bootstrap.Component
6605 * Bootstrap PaginationItem class
6606 * @cfg {String} html text
6607 * @cfg {String} href the link
6608 * @cfg {Boolean} preventDefault (true | false) default true
6609 * @cfg {Boolean} active (true | false) default false
6610 * @cfg {Boolean} disabled default false
6614 * Create a new PaginationItem
6615 * @param {Object} config The config object
6619 Roo.bootstrap.PaginationItem = function(config){
6620 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6625 * The raw click event for the entire grid.
6626 * @param {Roo.EventObject} e
6632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6636 preventDefault: true,
6641 getAutoCreate : function(){
6647 href : this.href ? this.href : '#',
6648 html : this.html ? this.html : ''
6658 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6662 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6668 initEvents: function() {
6670 this.el.on('click', this.onClick, this);
6673 onClick : function(e)
6675 Roo.log('PaginationItem on click ');
6676 if(this.preventDefault){
6684 this.fireEvent('click', this, e);
6700 * @class Roo.bootstrap.Slider
6701 * @extends Roo.bootstrap.Component
6702 * Bootstrap Slider class
6705 * Create a new Slider
6706 * @param {Object} config The config object
6709 Roo.bootstrap.Slider = function(config){
6710 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6715 getAutoCreate : function(){
6719 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6723 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6735 * Ext JS Library 1.1.1
6736 * Copyright(c) 2006-2007, Ext JS, LLC.
6738 * Originally Released Under LGPL - original licence link has changed is not relivant.
6741 * <script type="text/javascript">
6746 * @class Roo.grid.ColumnModel
6747 * @extends Roo.util.Observable
6748 * This is the default implementation of a ColumnModel used by the Grid. It defines
6749 * the columns in the grid.
6752 var colModel = new Roo.grid.ColumnModel([
6753 {header: "Ticker", width: 60, sortable: true, locked: true},
6754 {header: "Company Name", width: 150, sortable: true},
6755 {header: "Market Cap.", width: 100, sortable: true},
6756 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6757 {header: "Employees", width: 100, sortable: true, resizable: false}
6762 * The config options listed for this class are options which may appear in each
6763 * individual column definition.
6764 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6766 * @param {Object} config An Array of column config objects. See this class's
6767 * config objects for details.
6769 Roo.grid.ColumnModel = function(config){
6771 * The config passed into the constructor
6773 this.config = config;
6776 // if no id, create one
6777 // if the column does not have a dataIndex mapping,
6778 // map it to the order it is in the config
6779 for(var i = 0, len = config.length; i < len; i++){
6781 if(typeof c.dataIndex == "undefined"){
6784 if(typeof c.renderer == "string"){
6785 c.renderer = Roo.util.Format[c.renderer];
6787 if(typeof c.id == "undefined"){
6790 if(c.editor && c.editor.xtype){
6791 c.editor = Roo.factory(c.editor, Roo.grid);
6793 if(c.editor && c.editor.isFormField){
6794 c.editor = new Roo.grid.GridEditor(c.editor);
6796 this.lookup[c.id] = c;
6800 * The width of columns which have no width specified (defaults to 100)
6803 this.defaultWidth = 100;
6806 * Default sortable of columns which have no sortable specified (defaults to false)
6809 this.defaultSortable = false;
6813 * @event widthchange
6814 * Fires when the width of a column changes.
6815 * @param {ColumnModel} this
6816 * @param {Number} columnIndex The column index
6817 * @param {Number} newWidth The new width
6819 "widthchange": true,
6821 * @event headerchange
6822 * Fires when the text of a header changes.
6823 * @param {ColumnModel} this
6824 * @param {Number} columnIndex The column index
6825 * @param {Number} newText The new header text
6827 "headerchange": true,
6829 * @event hiddenchange
6830 * Fires when a column is hidden or "unhidden".
6831 * @param {ColumnModel} this
6832 * @param {Number} columnIndex The column index
6833 * @param {Boolean} hidden true if hidden, false otherwise
6835 "hiddenchange": true,
6837 * @event columnmoved
6838 * Fires when a column is moved.
6839 * @param {ColumnModel} this
6840 * @param {Number} oldIndex
6841 * @param {Number} newIndex
6843 "columnmoved" : true,
6845 * @event columlockchange
6846 * Fires when a column's locked state is changed
6847 * @param {ColumnModel} this
6848 * @param {Number} colIndex
6849 * @param {Boolean} locked true if locked
6851 "columnlockchange" : true
6853 Roo.grid.ColumnModel.superclass.constructor.call(this);
6855 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6857 * @cfg {String} header The header text to display in the Grid view.
6860 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6861 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6862 * specified, the column's index is used as an index into the Record's data Array.
6865 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6866 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6869 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6870 * Defaults to the value of the {@link #defaultSortable} property.
6871 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6874 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6877 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6880 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6883 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6886 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6887 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6888 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6889 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6892 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6895 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6898 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6901 * @cfg {String} cursor (Optional)
6904 * @cfg {String} tooltip (Optional)
6907 * @cfg {Number} xs (Optional)
6910 * @cfg {Number} sm (Optional)
6913 * @cfg {Number} md (Optional)
6916 * @cfg {Number} lg (Optional)
6919 * Returns the id of the column at the specified index.
6920 * @param {Number} index The column index
6921 * @return {String} the id
6923 getColumnId : function(index){
6924 return this.config[index].id;
6928 * Returns the column for a specified id.
6929 * @param {String} id The column id
6930 * @return {Object} the column
6932 getColumnById : function(id){
6933 return this.lookup[id];
6938 * Returns the column for a specified dataIndex.
6939 * @param {String} dataIndex The column dataIndex
6940 * @return {Object|Boolean} the column or false if not found
6942 getColumnByDataIndex: function(dataIndex){
6943 var index = this.findColumnIndex(dataIndex);
6944 return index > -1 ? this.config[index] : false;
6948 * Returns the index for a specified column id.
6949 * @param {String} id The column id
6950 * @return {Number} the index, or -1 if not found
6952 getIndexById : function(id){
6953 for(var i = 0, len = this.config.length; i < len; i++){
6954 if(this.config[i].id == id){
6962 * Returns the index for a specified column dataIndex.
6963 * @param {String} dataIndex The column dataIndex
6964 * @return {Number} the index, or -1 if not found
6967 findColumnIndex : function(dataIndex){
6968 for(var i = 0, len = this.config.length; i < len; i++){
6969 if(this.config[i].dataIndex == dataIndex){
6977 moveColumn : function(oldIndex, newIndex){
6978 var c = this.config[oldIndex];
6979 this.config.splice(oldIndex, 1);
6980 this.config.splice(newIndex, 0, c);
6981 this.dataMap = null;
6982 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6985 isLocked : function(colIndex){
6986 return this.config[colIndex].locked === true;
6989 setLocked : function(colIndex, value, suppressEvent){
6990 if(this.isLocked(colIndex) == value){
6993 this.config[colIndex].locked = value;
6995 this.fireEvent("columnlockchange", this, colIndex, value);
6999 getTotalLockedWidth : function(){
7001 for(var i = 0; i < this.config.length; i++){
7002 if(this.isLocked(i) && !this.isHidden(i)){
7003 this.totalWidth += this.getColumnWidth(i);
7009 getLockedCount : function(){
7010 for(var i = 0, len = this.config.length; i < len; i++){
7011 if(!this.isLocked(i)){
7016 return this.config.length;
7020 * Returns the number of columns.
7023 getColumnCount : function(visibleOnly){
7024 if(visibleOnly === true){
7026 for(var i = 0, len = this.config.length; i < len; i++){
7027 if(!this.isHidden(i)){
7033 return this.config.length;
7037 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7038 * @param {Function} fn
7039 * @param {Object} scope (optional)
7040 * @return {Array} result
7042 getColumnsBy : function(fn, scope){
7044 for(var i = 0, len = this.config.length; i < len; i++){
7045 var c = this.config[i];
7046 if(fn.call(scope||this, c, i) === true){
7054 * Returns true if the specified column is sortable.
7055 * @param {Number} col The column index
7058 isSortable : function(col){
7059 if(typeof this.config[col].sortable == "undefined"){
7060 return this.defaultSortable;
7062 return this.config[col].sortable;
7066 * Returns the rendering (formatting) function defined for the column.
7067 * @param {Number} col The column index.
7068 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7070 getRenderer : function(col){
7071 if(!this.config[col].renderer){
7072 return Roo.grid.ColumnModel.defaultRenderer;
7074 return this.config[col].renderer;
7078 * Sets the rendering (formatting) function for a column.
7079 * @param {Number} col The column index
7080 * @param {Function} fn The function to use to process the cell's raw data
7081 * to return HTML markup for the grid view. The render function is called with
7082 * the following parameters:<ul>
7083 * <li>Data value.</li>
7084 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7085 * <li>css A CSS style string to apply to the table cell.</li>
7086 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7087 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7088 * <li>Row index</li>
7089 * <li>Column index</li>
7090 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7092 setRenderer : function(col, fn){
7093 this.config[col].renderer = fn;
7097 * Returns the width for the specified column.
7098 * @param {Number} col The column index
7101 getColumnWidth : function(col){
7102 return this.config[col].width * 1 || this.defaultWidth;
7106 * Sets the width for a column.
7107 * @param {Number} col The column index
7108 * @param {Number} width The new width
7110 setColumnWidth : function(col, width, suppressEvent){
7111 this.config[col].width = width;
7112 this.totalWidth = null;
7114 this.fireEvent("widthchange", this, col, width);
7119 * Returns the total width of all columns.
7120 * @param {Boolean} includeHidden True to include hidden column widths
7123 getTotalWidth : function(includeHidden){
7124 if(!this.totalWidth){
7125 this.totalWidth = 0;
7126 for(var i = 0, len = this.config.length; i < len; i++){
7127 if(includeHidden || !this.isHidden(i)){
7128 this.totalWidth += this.getColumnWidth(i);
7132 return this.totalWidth;
7136 * Returns the header for the specified column.
7137 * @param {Number} col The column index
7140 getColumnHeader : function(col){
7141 return this.config[col].header;
7145 * Sets the header for a column.
7146 * @param {Number} col The column index
7147 * @param {String} header The new header
7149 setColumnHeader : function(col, header){
7150 this.config[col].header = header;
7151 this.fireEvent("headerchange", this, col, header);
7155 * Returns the tooltip for the specified column.
7156 * @param {Number} col The column index
7159 getColumnTooltip : function(col){
7160 return this.config[col].tooltip;
7163 * Sets the tooltip for a column.
7164 * @param {Number} col The column index
7165 * @param {String} tooltip The new tooltip
7167 setColumnTooltip : function(col, tooltip){
7168 this.config[col].tooltip = tooltip;
7172 * Returns the dataIndex for the specified column.
7173 * @param {Number} col The column index
7176 getDataIndex : function(col){
7177 return this.config[col].dataIndex;
7181 * Sets the dataIndex for a column.
7182 * @param {Number} col The column index
7183 * @param {Number} dataIndex The new dataIndex
7185 setDataIndex : function(col, dataIndex){
7186 this.config[col].dataIndex = dataIndex;
7192 * Returns true if the cell is editable.
7193 * @param {Number} colIndex The column index
7194 * @param {Number} rowIndex The row index - this is nto actually used..?
7197 isCellEditable : function(colIndex, rowIndex){
7198 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7202 * Returns the editor defined for the cell/column.
7203 * return false or null to disable editing.
7204 * @param {Number} colIndex The column index
7205 * @param {Number} rowIndex The row index
7208 getCellEditor : function(colIndex, rowIndex){
7209 return this.config[colIndex].editor;
7213 * Sets if a column is editable.
7214 * @param {Number} col The column index
7215 * @param {Boolean} editable True if the column is editable
7217 setEditable : function(col, editable){
7218 this.config[col].editable = editable;
7223 * Returns true if the column is hidden.
7224 * @param {Number} colIndex The column index
7227 isHidden : function(colIndex){
7228 return this.config[colIndex].hidden;
7233 * Returns true if the column width cannot be changed
7235 isFixed : function(colIndex){
7236 return this.config[colIndex].fixed;
7240 * Returns true if the column can be resized
7243 isResizable : function(colIndex){
7244 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7247 * Sets if a column is hidden.
7248 * @param {Number} colIndex The column index
7249 * @param {Boolean} hidden True if the column is hidden
7251 setHidden : function(colIndex, hidden){
7252 this.config[colIndex].hidden = hidden;
7253 this.totalWidth = null;
7254 this.fireEvent("hiddenchange", this, colIndex, hidden);
7258 * Sets the editor for a column.
7259 * @param {Number} col The column index
7260 * @param {Object} editor The editor object
7262 setEditor : function(col, editor){
7263 this.config[col].editor = editor;
7267 Roo.grid.ColumnModel.defaultRenderer = function(value)
7269 if(typeof value == "object") {
7272 if(typeof value == "string" && value.length < 1){
7276 return String.format("{0}", value);
7279 // Alias for backwards compatibility
7280 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7283 * Ext JS Library 1.1.1
7284 * Copyright(c) 2006-2007, Ext JS, LLC.
7286 * Originally Released Under LGPL - original licence link has changed is not relivant.
7289 * <script type="text/javascript">
7293 * @class Roo.LoadMask
7294 * A simple utility class for generically masking elements while loading data. If the element being masked has
7295 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7296 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7297 * element's UpdateManager load indicator and will be destroyed after the initial load.
7299 * Create a new LoadMask
7300 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7301 * @param {Object} config The config object
7303 Roo.LoadMask = function(el, config){
7304 this.el = Roo.get(el);
7305 Roo.apply(this, config);
7307 this.store.on('beforeload', this.onBeforeLoad, this);
7308 this.store.on('load', this.onLoad, this);
7309 this.store.on('loadexception', this.onLoadException, this);
7310 this.removeMask = false;
7312 var um = this.el.getUpdateManager();
7313 um.showLoadIndicator = false; // disable the default indicator
7314 um.on('beforeupdate', this.onBeforeLoad, this);
7315 um.on('update', this.onLoad, this);
7316 um.on('failure', this.onLoad, this);
7317 this.removeMask = true;
7321 Roo.LoadMask.prototype = {
7323 * @cfg {Boolean} removeMask
7324 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7325 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7329 * The text to display in a centered loading message box (defaults to 'Loading...')
7333 * @cfg {String} msgCls
7334 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7336 msgCls : 'x-mask-loading',
7339 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7345 * Disables the mask to prevent it from being displayed
7347 disable : function(){
7348 this.disabled = true;
7352 * Enables the mask so that it can be displayed
7354 enable : function(){
7355 this.disabled = false;
7358 onLoadException : function()
7362 if (typeof(arguments[3]) != 'undefined') {
7363 Roo.MessageBox.alert("Error loading",arguments[3]);
7367 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7368 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7375 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7380 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7384 onBeforeLoad : function(){
7386 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7391 destroy : function(){
7393 this.store.un('beforeload', this.onBeforeLoad, this);
7394 this.store.un('load', this.onLoad, this);
7395 this.store.un('loadexception', this.onLoadException, this);
7397 var um = this.el.getUpdateManager();
7398 um.un('beforeupdate', this.onBeforeLoad, this);
7399 um.un('update', this.onLoad, this);
7400 um.un('failure', this.onLoad, this);
7411 * @class Roo.bootstrap.Table
7412 * @extends Roo.bootstrap.Component
7413 * Bootstrap Table class
7414 * @cfg {String} cls table class
7415 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7416 * @cfg {String} bgcolor Specifies the background color for a table
7417 * @cfg {Number} border Specifies whether the table cells should have borders or not
7418 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7419 * @cfg {Number} cellspacing Specifies the space between cells
7420 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7421 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7422 * @cfg {String} sortable Specifies that the table should be sortable
7423 * @cfg {String} summary Specifies a summary of the content of a table
7424 * @cfg {Number} width Specifies the width of a table
7425 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7427 * @cfg {boolean} striped Should the rows be alternative striped
7428 * @cfg {boolean} bordered Add borders to the table
7429 * @cfg {boolean} hover Add hover highlighting
7430 * @cfg {boolean} condensed Format condensed
7431 * @cfg {boolean} responsive Format condensed
7432 * @cfg {Boolean} loadMask (true|false) default false
7433 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7434 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7435 * @cfg {Boolean} rowSelection (true|false) default false
7436 * @cfg {Boolean} cellSelection (true|false) default false
7437 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7438 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7439 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7440 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7444 * Create a new Table
7445 * @param {Object} config The config object
7448 Roo.bootstrap.Table = function(config){
7449 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7454 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7455 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7456 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7457 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7459 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7461 this.sm.grid = this;
7462 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7463 this.sm = this.selModel;
7464 this.sm.xmodule = this.xmodule || false;
7467 if (this.cm && typeof(this.cm.config) == 'undefined') {
7468 this.colModel = new Roo.grid.ColumnModel(this.cm);
7469 this.cm = this.colModel;
7470 this.cm.xmodule = this.xmodule || false;
7473 this.store= Roo.factory(this.store, Roo.data);
7474 this.ds = this.store;
7475 this.ds.xmodule = this.xmodule || false;
7478 if (this.footer && this.store) {
7479 this.footer.dataSource = this.ds;
7480 this.footer = Roo.factory(this.footer);
7487 * Fires when a cell is clicked
7488 * @param {Roo.bootstrap.Table} this
7489 * @param {Roo.Element} el
7490 * @param {Number} rowIndex
7491 * @param {Number} columnIndex
7492 * @param {Roo.EventObject} e
7496 * @event celldblclick
7497 * Fires when a cell is double clicked
7498 * @param {Roo.bootstrap.Table} this
7499 * @param {Roo.Element} el
7500 * @param {Number} rowIndex
7501 * @param {Number} columnIndex
7502 * @param {Roo.EventObject} e
7504 "celldblclick" : true,
7507 * Fires when a row is clicked
7508 * @param {Roo.bootstrap.Table} this
7509 * @param {Roo.Element} el
7510 * @param {Number} rowIndex
7511 * @param {Roo.EventObject} e
7515 * @event rowdblclick
7516 * Fires when a row is double clicked
7517 * @param {Roo.bootstrap.Table} this
7518 * @param {Roo.Element} el
7519 * @param {Number} rowIndex
7520 * @param {Roo.EventObject} e
7522 "rowdblclick" : true,
7525 * Fires when a mouseover occur
7526 * @param {Roo.bootstrap.Table} this
7527 * @param {Roo.Element} el
7528 * @param {Number} rowIndex
7529 * @param {Number} columnIndex
7530 * @param {Roo.EventObject} e
7535 * Fires when a mouseout occur
7536 * @param {Roo.bootstrap.Table} this
7537 * @param {Roo.Element} el
7538 * @param {Number} rowIndex
7539 * @param {Number} columnIndex
7540 * @param {Roo.EventObject} e
7545 * Fires when a row is rendered, so you can change add a style to it.
7546 * @param {Roo.bootstrap.Table} this
7547 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7551 * @event rowsrendered
7552 * Fires when all the rows have been rendered
7553 * @param {Roo.bootstrap.Table} this
7555 'rowsrendered' : true,
7557 * @event contextmenu
7558 * The raw contextmenu event for the entire grid.
7559 * @param {Roo.EventObject} e
7561 "contextmenu" : true,
7563 * @event rowcontextmenu
7564 * Fires when a row is right clicked
7565 * @param {Roo.bootstrap.Table} this
7566 * @param {Number} rowIndex
7567 * @param {Roo.EventObject} e
7569 "rowcontextmenu" : true,
7571 * @event cellcontextmenu
7572 * Fires when a cell is right clicked
7573 * @param {Roo.bootstrap.Table} this
7574 * @param {Number} rowIndex
7575 * @param {Number} cellIndex
7576 * @param {Roo.EventObject} e
7578 "cellcontextmenu" : true,
7580 * @event headercontextmenu
7581 * Fires when a header is right clicked
7582 * @param {Roo.bootstrap.Table} this
7583 * @param {Number} columnIndex
7584 * @param {Roo.EventObject} e
7586 "headercontextmenu" : true
7590 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7616 rowSelection : false,
7617 cellSelection : false,
7620 // Roo.Element - the tbody
7622 // Roo.Element - thead element
7625 container: false, // used by gridpanel...
7631 auto_hide_footer : false,
7633 getAutoCreate : function()
7635 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7642 if (this.scrollBody) {
7643 cfg.cls += ' table-body-fixed';
7646 cfg.cls += ' table-striped';
7650 cfg.cls += ' table-hover';
7652 if (this.bordered) {
7653 cfg.cls += ' table-bordered';
7655 if (this.condensed) {
7656 cfg.cls += ' table-condensed';
7658 if (this.responsive) {
7659 cfg.cls += ' table-responsive';
7663 cfg.cls+= ' ' +this.cls;
7666 // this lot should be simplifed...
7679 ].forEach(function(k) {
7687 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7690 if(this.store || this.cm){
7691 if(this.headerShow){
7692 cfg.cn.push(this.renderHeader());
7695 cfg.cn.push(this.renderBody());
7697 if(this.footerShow){
7698 cfg.cn.push(this.renderFooter());
7700 // where does this come from?
7701 //cfg.cls+= ' TableGrid';
7704 return { cn : [ cfg ] };
7707 initEvents : function()
7709 if(!this.store || !this.cm){
7712 if (this.selModel) {
7713 this.selModel.initEvents();
7717 //Roo.log('initEvents with ds!!!!');
7719 this.mainBody = this.el.select('tbody', true).first();
7720 this.mainHead = this.el.select('thead', true).first();
7721 this.mainFoot = this.el.select('tfoot', true).first();
7727 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7728 e.on('click', _this.sort, _this);
7731 this.mainBody.on("click", this.onClick, this);
7732 this.mainBody.on("dblclick", this.onDblClick, this);
7734 // why is this done????? = it breaks dialogs??
7735 //this.parent().el.setStyle('position', 'relative');
7739 this.footer.parentId = this.id;
7740 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7743 this.el.select('tfoot tr td').first().addClass('hide');
7748 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7751 this.store.on('load', this.onLoad, this);
7752 this.store.on('beforeload', this.onBeforeLoad, this);
7753 this.store.on('update', this.onUpdate, this);
7754 this.store.on('add', this.onAdd, this);
7755 this.store.on("clear", this.clear, this);
7757 this.el.on("contextmenu", this.onContextMenu, this);
7759 this.mainBody.on('scroll', this.onBodyScroll, this);
7761 this.cm.on("headerchange", this.onHeaderChange, this);
7763 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7767 onContextMenu : function(e, t)
7769 this.processEvent("contextmenu", e);
7772 processEvent : function(name, e)
7774 if (name != 'touchstart' ) {
7775 this.fireEvent(name, e);
7778 var t = e.getTarget();
7780 var cell = Roo.get(t);
7786 if(cell.findParent('tfoot', false, true)){
7790 if(cell.findParent('thead', false, true)){
7792 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7793 cell = Roo.get(t).findParent('th', false, true);
7795 Roo.log("failed to find th in thead?");
7796 Roo.log(e.getTarget());
7801 var cellIndex = cell.dom.cellIndex;
7803 var ename = name == 'touchstart' ? 'click' : name;
7804 this.fireEvent("header" + ename, this, cellIndex, e);
7809 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7810 cell = Roo.get(t).findParent('td', false, true);
7812 Roo.log("failed to find th in tbody?");
7813 Roo.log(e.getTarget());
7818 var row = cell.findParent('tr', false, true);
7819 var cellIndex = cell.dom.cellIndex;
7820 var rowIndex = row.dom.rowIndex - 1;
7824 this.fireEvent("row" + name, this, rowIndex, e);
7828 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7834 onMouseover : function(e, el)
7836 var cell = Roo.get(el);
7842 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7843 cell = cell.findParent('td', false, true);
7846 var row = cell.findParent('tr', false, true);
7847 var cellIndex = cell.dom.cellIndex;
7848 var rowIndex = row.dom.rowIndex - 1; // start from 0
7850 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7854 onMouseout : function(e, el)
7856 var cell = Roo.get(el);
7862 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7863 cell = cell.findParent('td', false, true);
7866 var row = cell.findParent('tr', false, true);
7867 var cellIndex = cell.dom.cellIndex;
7868 var rowIndex = row.dom.rowIndex - 1; // start from 0
7870 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7874 onClick : function(e, el)
7876 var cell = Roo.get(el);
7878 if(!cell || (!this.cellSelection && !this.rowSelection)){
7882 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7883 cell = cell.findParent('td', false, true);
7886 if(!cell || typeof(cell) == 'undefined'){
7890 var row = cell.findParent('tr', false, true);
7892 if(!row || typeof(row) == 'undefined'){
7896 var cellIndex = cell.dom.cellIndex;
7897 var rowIndex = this.getRowIndex(row);
7899 // why??? - should these not be based on SelectionModel?
7900 if(this.cellSelection){
7901 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7904 if(this.rowSelection){
7905 this.fireEvent('rowclick', this, row, rowIndex, e);
7911 onDblClick : function(e,el)
7913 var cell = Roo.get(el);
7915 if(!cell || (!this.cellSelection && !this.rowSelection)){
7919 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7920 cell = cell.findParent('td', false, true);
7923 if(!cell || typeof(cell) == 'undefined'){
7927 var row = cell.findParent('tr', false, true);
7929 if(!row || typeof(row) == 'undefined'){
7933 var cellIndex = cell.dom.cellIndex;
7934 var rowIndex = this.getRowIndex(row);
7936 if(this.cellSelection){
7937 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7940 if(this.rowSelection){
7941 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7945 sort : function(e,el)
7947 var col = Roo.get(el);
7949 if(!col.hasClass('sortable')){
7953 var sort = col.attr('sort');
7956 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7960 this.store.sortInfo = {field : sort, direction : dir};
7963 Roo.log("calling footer first");
7964 this.footer.onClick('first');
7967 this.store.load({ params : { start : 0 } });
7971 renderHeader : function()
7979 this.totalWidth = 0;
7981 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7983 var config = cm.config[i];
7987 cls : 'x-hcol-' + i,
7989 html: cm.getColumnHeader(i)
7994 if(typeof(config.sortable) != 'undefined' && config.sortable){
7996 c.html = '<i class="glyphicon"></i>' + c.html;
7999 // could use BS4 hidden-..-down
8001 if(typeof(config.lgHeader) != 'undefined'){
8002 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8005 if(typeof(config.mdHeader) != 'undefined'){
8006 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8009 if(typeof(config.smHeader) != 'undefined'){
8010 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8013 if(typeof(config.xsHeader) != 'undefined'){
8014 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8021 if(typeof(config.tooltip) != 'undefined'){
8022 c.tooltip = config.tooltip;
8025 if(typeof(config.colspan) != 'undefined'){
8026 c.colspan = config.colspan;
8029 if(typeof(config.hidden) != 'undefined' && config.hidden){
8030 c.style += ' display:none;';
8033 if(typeof(config.dataIndex) != 'undefined'){
8034 c.sort = config.dataIndex;
8039 if(typeof(config.align) != 'undefined' && config.align.length){
8040 c.style += ' text-align:' + config.align + ';';
8043 if(typeof(config.width) != 'undefined'){
8044 c.style += ' width:' + config.width + 'px;';
8045 this.totalWidth += config.width;
8047 this.totalWidth += 100; // assume minimum of 100 per column?
8050 if(typeof(config.cls) != 'undefined'){
8051 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8054 ['xs','sm','md','lg'].map(function(size){
8056 if(typeof(config[size]) == 'undefined'){
8060 if (!config[size]) { // 0 = hidden
8061 // BS 4 '0' is treated as hide that column and below.
8062 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8066 c.cls += ' col-' + size + '-' + config[size] + (
8067 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8079 renderBody : function()
8089 colspan : this.cm.getColumnCount()
8099 renderFooter : function()
8109 colspan : this.cm.getColumnCount()
8123 // Roo.log('ds onload');
8128 var ds = this.store;
8130 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8131 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8132 if (_this.store.sortInfo) {
8134 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8135 e.select('i', true).addClass(['glyphicon-arrow-up']);
8138 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8139 e.select('i', true).addClass(['glyphicon-arrow-down']);
8144 var tbody = this.mainBody;
8146 if(ds.getCount() > 0){
8147 ds.data.each(function(d,rowIndex){
8148 var row = this.renderRow(cm, ds, rowIndex);
8150 tbody.createChild(row);
8154 if(row.cellObjects.length){
8155 Roo.each(row.cellObjects, function(r){
8156 _this.renderCellObject(r);
8163 var tfoot = this.el.select('tfoot', true).first();
8165 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8167 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8169 var total = this.ds.getTotalCount();
8171 if(this.footer.pageSize < total){
8172 this.mainFoot.show();
8176 Roo.each(this.el.select('tbody td', true).elements, function(e){
8177 e.on('mouseover', _this.onMouseover, _this);
8180 Roo.each(this.el.select('tbody td', true).elements, function(e){
8181 e.on('mouseout', _this.onMouseout, _this);
8183 this.fireEvent('rowsrendered', this);
8189 onUpdate : function(ds,record)
8191 this.refreshRow(record);
8195 onRemove : function(ds, record, index, isUpdate){
8196 if(isUpdate !== true){
8197 this.fireEvent("beforerowremoved", this, index, record);
8199 var bt = this.mainBody.dom;
8201 var rows = this.el.select('tbody > tr', true).elements;
8203 if(typeof(rows[index]) != 'undefined'){
8204 bt.removeChild(rows[index].dom);
8207 // if(bt.rows[index]){
8208 // bt.removeChild(bt.rows[index]);
8211 if(isUpdate !== true){
8212 //this.stripeRows(index);
8213 //this.syncRowHeights(index, index);
8215 this.fireEvent("rowremoved", this, index, record);
8219 onAdd : function(ds, records, rowIndex)
8221 //Roo.log('on Add called');
8222 // - note this does not handle multiple adding very well..
8223 var bt = this.mainBody.dom;
8224 for (var i =0 ; i < records.length;i++) {
8225 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8226 //Roo.log(records[i]);
8227 //Roo.log(this.store.getAt(rowIndex+i));
8228 this.insertRow(this.store, rowIndex + i, false);
8235 refreshRow : function(record){
8236 var ds = this.store, index;
8237 if(typeof record == 'number'){
8239 record = ds.getAt(index);
8241 index = ds.indexOf(record);
8243 this.insertRow(ds, index, true);
8245 this.onRemove(ds, record, index+1, true);
8247 //this.syncRowHeights(index, index);
8249 this.fireEvent("rowupdated", this, index, record);
8252 insertRow : function(dm, rowIndex, isUpdate){
8255 this.fireEvent("beforerowsinserted", this, rowIndex);
8257 //var s = this.getScrollState();
8258 var row = this.renderRow(this.cm, this.store, rowIndex);
8259 // insert before rowIndex..
8260 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8264 if(row.cellObjects.length){
8265 Roo.each(row.cellObjects, function(r){
8266 _this.renderCellObject(r);
8271 this.fireEvent("rowsinserted", this, rowIndex);
8272 //this.syncRowHeights(firstRow, lastRow);
8273 //this.stripeRows(firstRow);
8280 getRowDom : function(rowIndex)
8282 var rows = this.el.select('tbody > tr', true).elements;
8284 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8287 // returns the object tree for a tr..
8290 renderRow : function(cm, ds, rowIndex)
8292 var d = ds.getAt(rowIndex);
8296 cls : 'x-row-' + rowIndex,
8300 var cellObjects = [];
8302 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8303 var config = cm.config[i];
8305 var renderer = cm.getRenderer(i);
8309 if(typeof(renderer) !== 'undefined'){
8310 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8312 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8313 // and are rendered into the cells after the row is rendered - using the id for the element.
8315 if(typeof(value) === 'object'){
8325 rowIndex : rowIndex,
8330 this.fireEvent('rowclass', this, rowcfg);
8334 cls : rowcfg.rowClass + ' x-col-' + i,
8336 html: (typeof(value) === 'object') ? '' : value
8343 if(typeof(config.colspan) != 'undefined'){
8344 td.colspan = config.colspan;
8347 if(typeof(config.hidden) != 'undefined' && config.hidden){
8348 td.style += ' display:none;';
8351 if(typeof(config.align) != 'undefined' && config.align.length){
8352 td.style += ' text-align:' + config.align + ';';
8354 if(typeof(config.valign) != 'undefined' && config.valign.length){
8355 td.style += ' vertical-align:' + config.valign + ';';
8358 if(typeof(config.width) != 'undefined'){
8359 td.style += ' width:' + config.width + 'px;';
8362 if(typeof(config.cursor) != 'undefined'){
8363 td.style += ' cursor:' + config.cursor + ';';
8366 if(typeof(config.cls) != 'undefined'){
8367 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8370 ['xs','sm','md','lg'].map(function(size){
8372 if(typeof(config[size]) == 'undefined'){
8378 if (!config[size]) { // 0 = hidden
8379 // BS 4 '0' is treated as hide that column and below.
8380 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8384 td.cls += ' col-' + size + '-' + config[size] + (
8385 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8395 row.cellObjects = cellObjects;
8403 onBeforeLoad : function()
8412 this.el.select('tbody', true).first().dom.innerHTML = '';
8415 * Show or hide a row.
8416 * @param {Number} rowIndex to show or hide
8417 * @param {Boolean} state hide
8419 setRowVisibility : function(rowIndex, state)
8421 var bt = this.mainBody.dom;
8423 var rows = this.el.select('tbody > tr', true).elements;
8425 if(typeof(rows[rowIndex]) == 'undefined'){
8428 rows[rowIndex].dom.style.display = state ? '' : 'none';
8432 getSelectionModel : function(){
8434 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8436 return this.selModel;
8439 * Render the Roo.bootstrap object from renderder
8441 renderCellObject : function(r)
8445 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8447 var t = r.cfg.render(r.container);
8450 Roo.each(r.cfg.cn, function(c){
8452 container: t.getChildContainer(),
8455 _this.renderCellObject(child);
8460 getRowIndex : function(row)
8464 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8475 * Returns the grid's underlying element = used by panel.Grid
8476 * @return {Element} The element
8478 getGridEl : function(){
8482 * Forces a resize - used by panel.Grid
8483 * @return {Element} The element
8485 autoSize : function()
8487 //var ctr = Roo.get(this.container.dom.parentElement);
8488 var ctr = Roo.get(this.el.dom);
8490 var thd = this.getGridEl().select('thead',true).first();
8491 var tbd = this.getGridEl().select('tbody', true).first();
8492 var tfd = this.getGridEl().select('tfoot', true).first();
8494 var cw = ctr.getWidth();
8498 tbd.setWidth(ctr.getWidth());
8499 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8500 // this needs fixing for various usage - currently only hydra job advers I think..
8502 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8504 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8507 cw = Math.max(cw, this.totalWidth);
8508 this.getGridEl().select('tr',true).setWidth(cw);
8509 // resize 'expandable coloumn?
8511 return; // we doe not have a view in this design..
8514 onBodyScroll: function()
8516 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8518 this.mainHead.setStyle({
8519 'position' : 'relative',
8520 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8526 var scrollHeight = this.mainBody.dom.scrollHeight;
8528 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8530 var height = this.mainBody.getHeight();
8532 if(scrollHeight - height == scrollTop) {
8534 var total = this.ds.getTotalCount();
8536 if(this.footer.cursor + this.footer.pageSize < total){
8538 this.footer.ds.load({
8540 start : this.footer.cursor + this.footer.pageSize,
8541 limit : this.footer.pageSize
8551 onHeaderChange : function()
8553 var header = this.renderHeader();
8554 var table = this.el.select('table', true).first();
8556 this.mainHead.remove();
8557 this.mainHead = table.createChild(header, this.mainBody, false);
8560 onHiddenChange : function(colModel, colIndex, hidden)
8562 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8563 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8565 this.CSS.updateRule(thSelector, "display", "");
8566 this.CSS.updateRule(tdSelector, "display", "");
8569 this.CSS.updateRule(thSelector, "display", "none");
8570 this.CSS.updateRule(tdSelector, "display", "none");
8573 this.onHeaderChange();
8577 setColumnWidth: function(col_index, width)
8579 // width = "md-2 xs-2..."
8580 if(!this.colModel.config[col_index]) {
8584 var w = width.split(" ");
8586 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8588 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8591 for(var j = 0; j < w.length; j++) {
8597 var size_cls = w[j].split("-");
8599 if(!Number.isInteger(size_cls[1] * 1)) {
8603 if(!this.colModel.config[col_index][size_cls[0]]) {
8607 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8611 h_row[0].classList.replace(
8612 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8613 "col-"+size_cls[0]+"-"+size_cls[1]
8616 for(var i = 0; i < rows.length; i++) {
8618 var size_cls = w[j].split("-");
8620 if(!Number.isInteger(size_cls[1] * 1)) {
8624 if(!this.colModel.config[col_index][size_cls[0]]) {
8628 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8632 rows[i].classList.replace(
8633 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8634 "col-"+size_cls[0]+"-"+size_cls[1]
8638 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8653 * @class Roo.bootstrap.TableCell
8654 * @extends Roo.bootstrap.Component
8655 * Bootstrap TableCell class
8656 * @cfg {String} html cell contain text
8657 * @cfg {String} cls cell class
8658 * @cfg {String} tag cell tag (td|th) default td
8659 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8660 * @cfg {String} align Aligns the content in a cell
8661 * @cfg {String} axis Categorizes cells
8662 * @cfg {String} bgcolor Specifies the background color of a cell
8663 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8664 * @cfg {Number} colspan Specifies the number of columns a cell should span
8665 * @cfg {String} headers Specifies one or more header cells a cell is related to
8666 * @cfg {Number} height Sets the height of a cell
8667 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8668 * @cfg {Number} rowspan Sets the number of rows a cell should span
8669 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8670 * @cfg {String} valign Vertical aligns the content in a cell
8671 * @cfg {Number} width Specifies the width of a cell
8674 * Create a new TableCell
8675 * @param {Object} config The config object
8678 Roo.bootstrap.TableCell = function(config){
8679 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8682 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8702 getAutoCreate : function(){
8703 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8723 cfg.align=this.align
8729 cfg.bgcolor=this.bgcolor
8732 cfg.charoff=this.charoff
8735 cfg.colspan=this.colspan
8738 cfg.headers=this.headers
8741 cfg.height=this.height
8744 cfg.nowrap=this.nowrap
8747 cfg.rowspan=this.rowspan
8750 cfg.scope=this.scope
8753 cfg.valign=this.valign
8756 cfg.width=this.width
8775 * @class Roo.bootstrap.TableRow
8776 * @extends Roo.bootstrap.Component
8777 * Bootstrap TableRow class
8778 * @cfg {String} cls row class
8779 * @cfg {String} align Aligns the content in a table row
8780 * @cfg {String} bgcolor Specifies a background color for a table row
8781 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8782 * @cfg {String} valign Vertical aligns the content in a table row
8785 * Create a new TableRow
8786 * @param {Object} config The config object
8789 Roo.bootstrap.TableRow = function(config){
8790 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8793 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8801 getAutoCreate : function(){
8802 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8812 cfg.align = this.align;
8815 cfg.bgcolor = this.bgcolor;
8818 cfg.charoff = this.charoff;
8821 cfg.valign = this.valign;
8839 * @class Roo.bootstrap.TableBody
8840 * @extends Roo.bootstrap.Component
8841 * Bootstrap TableBody class
8842 * @cfg {String} cls element class
8843 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8844 * @cfg {String} align Aligns the content inside the element
8845 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8846 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8849 * Create a new TableBody
8850 * @param {Object} config The config object
8853 Roo.bootstrap.TableBody = function(config){
8854 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8857 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8865 getAutoCreate : function(){
8866 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8880 cfg.align = this.align;
8883 cfg.charoff = this.charoff;
8886 cfg.valign = this.valign;
8893 // initEvents : function()
8900 // this.store = Roo.factory(this.store, Roo.data);
8901 // this.store.on('load', this.onLoad, this);
8903 // this.store.load();
8907 // onLoad: function ()
8909 // this.fireEvent('load', this);
8919 * Ext JS Library 1.1.1
8920 * Copyright(c) 2006-2007, Ext JS, LLC.
8922 * Originally Released Under LGPL - original licence link has changed is not relivant.
8925 * <script type="text/javascript">
8928 // as we use this in bootstrap.
8929 Roo.namespace('Roo.form');
8931 * @class Roo.form.Action
8932 * Internal Class used to handle form actions
8934 * @param {Roo.form.BasicForm} el The form element or its id
8935 * @param {Object} config Configuration options
8940 // define the action interface
8941 Roo.form.Action = function(form, options){
8943 this.options = options || {};
8946 * Client Validation Failed
8949 Roo.form.Action.CLIENT_INVALID = 'client';
8951 * Server Validation Failed
8954 Roo.form.Action.SERVER_INVALID = 'server';
8956 * Connect to Server Failed
8959 Roo.form.Action.CONNECT_FAILURE = 'connect';
8961 * Reading Data from Server Failed
8964 Roo.form.Action.LOAD_FAILURE = 'load';
8966 Roo.form.Action.prototype = {
8968 failureType : undefined,
8969 response : undefined,
8973 run : function(options){
8978 success : function(response){
8983 handleResponse : function(response){
8987 // default connection failure
8988 failure : function(response){
8990 this.response = response;
8991 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8992 this.form.afterAction(this, false);
8995 processResponse : function(response){
8996 this.response = response;
8997 if(!response.responseText){
9000 this.result = this.handleResponse(response);
9004 // utility functions used internally
9005 getUrl : function(appendParams){
9006 var url = this.options.url || this.form.url || this.form.el.dom.action;
9008 var p = this.getParams();
9010 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9016 getMethod : function(){
9017 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9020 getParams : function(){
9021 var bp = this.form.baseParams;
9022 var p = this.options.params;
9024 if(typeof p == "object"){
9025 p = Roo.urlEncode(Roo.applyIf(p, bp));
9026 }else if(typeof p == 'string' && bp){
9027 p += '&' + Roo.urlEncode(bp);
9030 p = Roo.urlEncode(bp);
9035 createCallback : function(){
9037 success: this.success,
9038 failure: this.failure,
9040 timeout: (this.form.timeout*1000),
9041 upload: this.form.fileUpload ? this.success : undefined
9046 Roo.form.Action.Submit = function(form, options){
9047 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9050 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9053 haveProgress : false,
9054 uploadComplete : false,
9056 // uploadProgress indicator.
9057 uploadProgress : function()
9059 if (!this.form.progressUrl) {
9063 if (!this.haveProgress) {
9064 Roo.MessageBox.progress("Uploading", "Uploading");
9066 if (this.uploadComplete) {
9067 Roo.MessageBox.hide();
9071 this.haveProgress = true;
9073 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9075 var c = new Roo.data.Connection();
9077 url : this.form.progressUrl,
9082 success : function(req){
9083 //console.log(data);
9087 rdata = Roo.decode(req.responseText)
9089 Roo.log("Invalid data from server..");
9093 if (!rdata || !rdata.success) {
9095 Roo.MessageBox.alert(Roo.encode(rdata));
9098 var data = rdata.data;
9100 if (this.uploadComplete) {
9101 Roo.MessageBox.hide();
9106 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9107 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9110 this.uploadProgress.defer(2000,this);
9113 failure: function(data) {
9114 Roo.log('progress url failed ');
9125 // run get Values on the form, so it syncs any secondary forms.
9126 this.form.getValues();
9128 var o = this.options;
9129 var method = this.getMethod();
9130 var isPost = method == 'POST';
9131 if(o.clientValidation === false || this.form.isValid()){
9133 if (this.form.progressUrl) {
9134 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9135 (new Date() * 1) + '' + Math.random());
9140 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9141 form:this.form.el.dom,
9142 url:this.getUrl(!isPost),
9144 params:isPost ? this.getParams() : null,
9145 isUpload: this.form.fileUpload,
9146 formData : this.form.formData
9149 this.uploadProgress();
9151 }else if (o.clientValidation !== false){ // client validation failed
9152 this.failureType = Roo.form.Action.CLIENT_INVALID;
9153 this.form.afterAction(this, false);
9157 success : function(response)
9159 this.uploadComplete= true;
9160 if (this.haveProgress) {
9161 Roo.MessageBox.hide();
9165 var result = this.processResponse(response);
9166 if(result === true || result.success){
9167 this.form.afterAction(this, true);
9171 this.form.markInvalid(result.errors);
9172 this.failureType = Roo.form.Action.SERVER_INVALID;
9174 this.form.afterAction(this, false);
9176 failure : function(response)
9178 this.uploadComplete= true;
9179 if (this.haveProgress) {
9180 Roo.MessageBox.hide();
9183 this.response = response;
9184 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9185 this.form.afterAction(this, false);
9188 handleResponse : function(response){
9189 if(this.form.errorReader){
9190 var rs = this.form.errorReader.read(response);
9193 for(var i = 0, len = rs.records.length; i < len; i++) {
9194 var r = rs.records[i];
9198 if(errors.length < 1){
9202 success : rs.success,
9208 ret = Roo.decode(response.responseText);
9212 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9222 Roo.form.Action.Load = function(form, options){
9223 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9224 this.reader = this.form.reader;
9227 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9232 Roo.Ajax.request(Roo.apply(
9233 this.createCallback(), {
9234 method:this.getMethod(),
9235 url:this.getUrl(false),
9236 params:this.getParams()
9240 success : function(response){
9242 var result = this.processResponse(response);
9243 if(result === true || !result.success || !result.data){
9244 this.failureType = Roo.form.Action.LOAD_FAILURE;
9245 this.form.afterAction(this, false);
9248 this.form.clearInvalid();
9249 this.form.setValues(result.data);
9250 this.form.afterAction(this, true);
9253 handleResponse : function(response){
9254 if(this.form.reader){
9255 var rs = this.form.reader.read(response);
9256 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9258 success : rs.success,
9262 return Roo.decode(response.responseText);
9266 Roo.form.Action.ACTION_TYPES = {
9267 'load' : Roo.form.Action.Load,
9268 'submit' : Roo.form.Action.Submit
9277 * @class Roo.bootstrap.Form
9278 * @extends Roo.bootstrap.Component
9279 * Bootstrap Form class
9280 * @cfg {String} method GET | POST (default POST)
9281 * @cfg {String} labelAlign top | left (default top)
9282 * @cfg {String} align left | right - for navbars
9283 * @cfg {Boolean} loadMask load mask when submit (default true)
9288 * @param {Object} config The config object
9292 Roo.bootstrap.Form = function(config){
9294 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9296 Roo.bootstrap.Form.popover.apply();
9300 * @event clientvalidation
9301 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9302 * @param {Form} this
9303 * @param {Boolean} valid true if the form has passed client-side validation
9305 clientvalidation: true,
9307 * @event beforeaction
9308 * Fires before any action is performed. Return false to cancel the action.
9309 * @param {Form} this
9310 * @param {Action} action The action to be performed
9314 * @event actionfailed
9315 * Fires when an action fails.
9316 * @param {Form} this
9317 * @param {Action} action The action that failed
9319 actionfailed : true,
9321 * @event actioncomplete
9322 * Fires when an action is completed.
9323 * @param {Form} this
9324 * @param {Action} action The action that completed
9326 actioncomplete : true
9330 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9333 * @cfg {String} method
9334 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9339 * The URL to use for form actions if one isn't supplied in the action options.
9342 * @cfg {Boolean} fileUpload
9343 * Set to true if this form is a file upload.
9347 * @cfg {Object} baseParams
9348 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9352 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9356 * @cfg {Sting} align (left|right) for navbar forms
9361 activeAction : null,
9364 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9365 * element by passing it or its id or mask the form itself by passing in true.
9368 waitMsgTarget : false,
9373 * @cfg {Boolean} errorMask (true|false) default false
9378 * @cfg {Number} maskOffset Default 100
9383 * @cfg {Boolean} maskBody
9387 getAutoCreate : function(){
9391 method : this.method || 'POST',
9392 id : this.id || Roo.id(),
9395 if (this.parent().xtype.match(/^Nav/)) {
9396 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9400 if (this.labelAlign == 'left' ) {
9401 cfg.cls += ' form-horizontal';
9407 initEvents : function()
9409 this.el.on('submit', this.onSubmit, this);
9410 // this was added as random key presses on the form where triggering form submit.
9411 this.el.on('keypress', function(e) {
9412 if (e.getCharCode() != 13) {
9415 // we might need to allow it for textareas.. and some other items.
9416 // check e.getTarget().
9418 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9422 Roo.log("keypress blocked");
9430 onSubmit : function(e){
9435 * Returns true if client-side validation on the form is successful.
9438 isValid : function(){
9439 var items = this.getItems();
9443 items.each(function(f){
9449 Roo.log('invalid field: ' + f.name);
9453 if(!target && f.el.isVisible(true)){
9459 if(this.errorMask && !valid){
9460 Roo.bootstrap.Form.popover.mask(this, target);
9467 * Returns true if any fields in this form have changed since their original load.
9470 isDirty : function(){
9472 var items = this.getItems();
9473 items.each(function(f){
9483 * Performs a predefined action (submit or load) or custom actions you define on this form.
9484 * @param {String} actionName The name of the action type
9485 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9486 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9487 * accept other config options):
9489 Property Type Description
9490 ---------------- --------------- ----------------------------------------------------------------------------------
9491 url String The url for the action (defaults to the form's url)
9492 method String The form method to use (defaults to the form's method, or POST if not defined)
9493 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9494 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9495 validate the form on the client (defaults to false)
9497 * @return {BasicForm} this
9499 doAction : function(action, options){
9500 if(typeof action == 'string'){
9501 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9503 if(this.fireEvent('beforeaction', this, action) !== false){
9504 this.beforeAction(action);
9505 action.run.defer(100, action);
9511 beforeAction : function(action){
9512 var o = action.options;
9517 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9519 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9522 // not really supported yet.. ??
9524 //if(this.waitMsgTarget === true){
9525 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9526 //}else if(this.waitMsgTarget){
9527 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9528 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9530 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9536 afterAction : function(action, success){
9537 this.activeAction = null;
9538 var o = action.options;
9543 Roo.get(document.body).unmask();
9549 //if(this.waitMsgTarget === true){
9550 // this.el.unmask();
9551 //}else if(this.waitMsgTarget){
9552 // this.waitMsgTarget.unmask();
9554 // Roo.MessageBox.updateProgress(1);
9555 // Roo.MessageBox.hide();
9562 Roo.callback(o.success, o.scope, [this, action]);
9563 this.fireEvent('actioncomplete', this, action);
9567 // failure condition..
9568 // we have a scenario where updates need confirming.
9569 // eg. if a locking scenario exists..
9570 // we look for { errors : { needs_confirm : true }} in the response.
9572 (typeof(action.result) != 'undefined') &&
9573 (typeof(action.result.errors) != 'undefined') &&
9574 (typeof(action.result.errors.needs_confirm) != 'undefined')
9577 Roo.log("not supported yet");
9580 Roo.MessageBox.confirm(
9581 "Change requires confirmation",
9582 action.result.errorMsg,
9587 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9597 Roo.callback(o.failure, o.scope, [this, action]);
9598 // show an error message if no failed handler is set..
9599 if (!this.hasListener('actionfailed')) {
9600 Roo.log("need to add dialog support");
9602 Roo.MessageBox.alert("Error",
9603 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9604 action.result.errorMsg :
9605 "Saving Failed, please check your entries or try again"
9610 this.fireEvent('actionfailed', this, action);
9615 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9616 * @param {String} id The value to search for
9619 findField : function(id){
9620 var items = this.getItems();
9621 var field = items.get(id);
9623 items.each(function(f){
9624 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9631 return field || null;
9634 * Mark fields in this form invalid in bulk.
9635 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9636 * @return {BasicForm} this
9638 markInvalid : function(errors){
9639 if(errors instanceof Array){
9640 for(var i = 0, len = errors.length; i < len; i++){
9641 var fieldError = errors[i];
9642 var f = this.findField(fieldError.id);
9644 f.markInvalid(fieldError.msg);
9650 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9651 field.markInvalid(errors[id]);
9655 //Roo.each(this.childForms || [], function (f) {
9656 // f.markInvalid(errors);
9663 * Set values for fields in this form in bulk.
9664 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9665 * @return {BasicForm} this
9667 setValues : function(values){
9668 if(values instanceof Array){ // array of objects
9669 for(var i = 0, len = values.length; i < len; i++){
9671 var f = this.findField(v.id);
9673 f.setValue(v.value);
9674 if(this.trackResetOnLoad){
9675 f.originalValue = f.getValue();
9679 }else{ // object hash
9682 if(typeof values[id] != 'function' && (field = this.findField(id))){
9684 if (field.setFromData &&
9686 field.displayField &&
9687 // combos' with local stores can
9688 // be queried via setValue()
9689 // to set their value..
9690 (field.store && !field.store.isLocal)
9694 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9695 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9696 field.setFromData(sd);
9698 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9700 field.setFromData(values);
9703 field.setValue(values[id]);
9707 if(this.trackResetOnLoad){
9708 field.originalValue = field.getValue();
9714 //Roo.each(this.childForms || [], function (f) {
9715 // f.setValues(values);
9722 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9723 * they are returned as an array.
9724 * @param {Boolean} asString
9727 getValues : function(asString){
9728 //if (this.childForms) {
9729 // copy values from the child forms
9730 // Roo.each(this.childForms, function (f) {
9731 // this.setValues(f.getValues());
9737 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9738 if(asString === true){
9741 return Roo.urlDecode(fs);
9745 * Returns the fields in this form as an object with key/value pairs.
9746 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9749 getFieldValues : function(with_hidden)
9751 var items = this.getItems();
9753 items.each(function(f){
9759 var v = f.getValue();
9761 if (f.inputType =='radio') {
9762 if (typeof(ret[f.getName()]) == 'undefined') {
9763 ret[f.getName()] = ''; // empty..
9766 if (!f.el.dom.checked) {
9774 if(f.xtype == 'MoneyField'){
9775 ret[f.currencyName] = f.getCurrency();
9778 // not sure if this supported any more..
9779 if ((typeof(v) == 'object') && f.getRawValue) {
9780 v = f.getRawValue() ; // dates..
9782 // combo boxes where name != hiddenName...
9783 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9784 ret[f.name] = f.getRawValue();
9786 ret[f.getName()] = v;
9793 * Clears all invalid messages in this form.
9794 * @return {BasicForm} this
9796 clearInvalid : function(){
9797 var items = this.getItems();
9799 items.each(function(f){
9808 * @return {BasicForm} this
9811 var items = this.getItems();
9812 items.each(function(f){
9816 Roo.each(this.childForms || [], function (f) {
9824 getItems : function()
9826 var r=new Roo.util.MixedCollection(false, function(o){
9827 return o.id || (o.id = Roo.id());
9829 var iter = function(el) {
9836 Roo.each(el.items,function(e) {
9845 hideFields : function(items)
9847 Roo.each(items, function(i){
9849 var f = this.findField(i);
9860 showFields : function(items)
9862 Roo.each(items, function(i){
9864 var f = this.findField(i);
9877 Roo.apply(Roo.bootstrap.Form, {
9904 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9905 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9906 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9907 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9910 this.maskEl.top.enableDisplayMode("block");
9911 this.maskEl.left.enableDisplayMode("block");
9912 this.maskEl.bottom.enableDisplayMode("block");
9913 this.maskEl.right.enableDisplayMode("block");
9915 this.toolTip = new Roo.bootstrap.Tooltip({
9916 cls : 'roo-form-error-popover',
9918 'left' : ['r-l', [-2,0], 'right'],
9919 'right' : ['l-r', [2,0], 'left'],
9920 'bottom' : ['tl-bl', [0,2], 'top'],
9921 'top' : [ 'bl-tl', [0,-2], 'bottom']
9925 this.toolTip.render(Roo.get(document.body));
9927 this.toolTip.el.enableDisplayMode("block");
9929 Roo.get(document.body).on('click', function(){
9933 Roo.get(document.body).on('touchstart', function(){
9937 this.isApplied = true
9940 mask : function(form, target)
9944 this.target = target;
9946 if(!this.form.errorMask || !target.el){
9950 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9952 Roo.log(scrollable);
9954 var ot = this.target.el.calcOffsetsTo(scrollable);
9956 var scrollTo = ot[1] - this.form.maskOffset;
9958 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9960 scrollable.scrollTo('top', scrollTo);
9962 var box = this.target.el.getBox();
9964 var zIndex = Roo.bootstrap.Modal.zIndex++;
9967 this.maskEl.top.setStyle('position', 'absolute');
9968 this.maskEl.top.setStyle('z-index', zIndex);
9969 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9970 this.maskEl.top.setLeft(0);
9971 this.maskEl.top.setTop(0);
9972 this.maskEl.top.show();
9974 this.maskEl.left.setStyle('position', 'absolute');
9975 this.maskEl.left.setStyle('z-index', zIndex);
9976 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9977 this.maskEl.left.setLeft(0);
9978 this.maskEl.left.setTop(box.y - this.padding);
9979 this.maskEl.left.show();
9981 this.maskEl.bottom.setStyle('position', 'absolute');
9982 this.maskEl.bottom.setStyle('z-index', zIndex);
9983 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9984 this.maskEl.bottom.setLeft(0);
9985 this.maskEl.bottom.setTop(box.bottom + this.padding);
9986 this.maskEl.bottom.show();
9988 this.maskEl.right.setStyle('position', 'absolute');
9989 this.maskEl.right.setStyle('z-index', zIndex);
9990 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9991 this.maskEl.right.setLeft(box.right + this.padding);
9992 this.maskEl.right.setTop(box.y - this.padding);
9993 this.maskEl.right.show();
9995 this.toolTip.bindEl = this.target.el;
9997 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9999 var tip = this.target.blankText;
10001 if(this.target.getValue() !== '' ) {
10003 if (this.target.invalidText.length) {
10004 tip = this.target.invalidText;
10005 } else if (this.target.regexText.length){
10006 tip = this.target.regexText;
10010 this.toolTip.show(tip);
10012 this.intervalID = window.setInterval(function() {
10013 Roo.bootstrap.Form.popover.unmask();
10016 window.onwheel = function(){ return false;};
10018 (function(){ this.isMasked = true; }).defer(500, this);
10022 unmask : function()
10024 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10028 this.maskEl.top.setStyle('position', 'absolute');
10029 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10030 this.maskEl.top.hide();
10032 this.maskEl.left.setStyle('position', 'absolute');
10033 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10034 this.maskEl.left.hide();
10036 this.maskEl.bottom.setStyle('position', 'absolute');
10037 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10038 this.maskEl.bottom.hide();
10040 this.maskEl.right.setStyle('position', 'absolute');
10041 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10042 this.maskEl.right.hide();
10044 this.toolTip.hide();
10046 this.toolTip.el.hide();
10048 window.onwheel = function(){ return true;};
10050 if(this.intervalID){
10051 window.clearInterval(this.intervalID);
10052 this.intervalID = false;
10055 this.isMasked = false;
10065 * Ext JS Library 1.1.1
10066 * Copyright(c) 2006-2007, Ext JS, LLC.
10068 * Originally Released Under LGPL - original licence link has changed is not relivant.
10071 * <script type="text/javascript">
10074 * @class Roo.form.VTypes
10075 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10078 Roo.form.VTypes = function(){
10079 // closure these in so they are only created once.
10080 var alpha = /^[a-zA-Z_]+$/;
10081 var alphanum = /^[a-zA-Z0-9_]+$/;
10082 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10083 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10085 // All these messages and functions are configurable
10088 * The function used to validate email addresses
10089 * @param {String} value The email address
10091 'email' : function(v){
10092 return email.test(v);
10095 * The error text to display when the email validation function returns false
10098 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10100 * The keystroke filter mask to be applied on email input
10103 'emailMask' : /[a-z0-9_\.\-@]/i,
10106 * The function used to validate URLs
10107 * @param {String} value The URL
10109 'url' : function(v){
10110 return url.test(v);
10113 * The error text to display when the url validation function returns false
10116 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10119 * The function used to validate alpha values
10120 * @param {String} value The value
10122 'alpha' : function(v){
10123 return alpha.test(v);
10126 * The error text to display when the alpha validation function returns false
10129 'alphaText' : 'This field should only contain letters and _',
10131 * The keystroke filter mask to be applied on alpha input
10134 'alphaMask' : /[a-z_]/i,
10137 * The function used to validate alphanumeric values
10138 * @param {String} value The value
10140 'alphanum' : function(v){
10141 return alphanum.test(v);
10144 * The error text to display when the alphanumeric validation function returns false
10147 'alphanumText' : 'This field should only contain letters, numbers and _',
10149 * The keystroke filter mask to be applied on alphanumeric input
10152 'alphanumMask' : /[a-z0-9_]/i
10162 * @class Roo.bootstrap.Input
10163 * @extends Roo.bootstrap.Component
10164 * Bootstrap Input class
10165 * @cfg {Boolean} disabled is it disabled
10166 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10167 * @cfg {String} name name of the input
10168 * @cfg {string} fieldLabel - the label associated
10169 * @cfg {string} placeholder - placeholder to put in text.
10170 * @cfg {string} before - input group add on before
10171 * @cfg {string} after - input group add on after
10172 * @cfg {string} size - (lg|sm) or leave empty..
10173 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10174 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10175 * @cfg {Number} md colspan out of 12 for computer-sized screens
10176 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10177 * @cfg {string} value default value of the input
10178 * @cfg {Number} labelWidth set the width of label
10179 * @cfg {Number} labellg set the width of label (1-12)
10180 * @cfg {Number} labelmd set the width of label (1-12)
10181 * @cfg {Number} labelsm set the width of label (1-12)
10182 * @cfg {Number} labelxs set the width of label (1-12)
10183 * @cfg {String} labelAlign (top|left)
10184 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10185 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10186 * @cfg {String} indicatorpos (left|right) default left
10187 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10188 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10189 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10191 * @cfg {String} align (left|center|right) Default left
10192 * @cfg {Boolean} forceFeedback (true|false) Default false
10195 * Create a new Input
10196 * @param {Object} config The config object
10199 Roo.bootstrap.Input = function(config){
10201 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10206 * Fires when this field receives input focus.
10207 * @param {Roo.form.Field} this
10212 * Fires when this field loses input focus.
10213 * @param {Roo.form.Field} this
10217 * @event specialkey
10218 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10219 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10220 * @param {Roo.form.Field} this
10221 * @param {Roo.EventObject} e The event object
10226 * Fires just before the field blurs if the field value has changed.
10227 * @param {Roo.form.Field} this
10228 * @param {Mixed} newValue The new value
10229 * @param {Mixed} oldValue The original value
10234 * Fires after the field has been marked as invalid.
10235 * @param {Roo.form.Field} this
10236 * @param {String} msg The validation message
10241 * Fires after the field has been validated with no errors.
10242 * @param {Roo.form.Field} this
10247 * Fires after the key up
10248 * @param {Roo.form.Field} this
10249 * @param {Roo.EventObject} e The event Object
10255 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10257 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10258 automatic validation (defaults to "keyup").
10260 validationEvent : "keyup",
10262 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10264 validateOnBlur : true,
10266 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10268 validationDelay : 250,
10270 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10272 focusClass : "x-form-focus", // not needed???
10276 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10278 invalidClass : "has-warning",
10281 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10283 validClass : "has-success",
10286 * @cfg {Boolean} hasFeedback (true|false) default true
10288 hasFeedback : true,
10291 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10293 invalidFeedbackClass : "glyphicon-warning-sign",
10296 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10298 validFeedbackClass : "glyphicon-ok",
10301 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10303 selectOnFocus : false,
10306 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10310 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10315 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10317 disableKeyFilter : false,
10320 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10324 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10328 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10330 blankText : "Please complete this mandatory field",
10333 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10337 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10339 maxLength : Number.MAX_VALUE,
10341 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10343 minLengthText : "The minimum length for this field is {0}",
10345 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10347 maxLengthText : "The maximum length for this field is {0}",
10351 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10352 * If available, this function will be called only after the basic validators all return true, and will be passed the
10353 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10357 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10358 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10359 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10363 * @cfg {String} regexText -- Depricated - use Invalid Text
10368 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10374 autocomplete: false,
10378 inputType : 'text',
10381 placeholder: false,
10386 preventMark: false,
10387 isFormField : true,
10390 labelAlign : false,
10393 formatedValue : false,
10394 forceFeedback : false,
10396 indicatorpos : 'left',
10406 parentLabelAlign : function()
10409 while (parent.parent()) {
10410 parent = parent.parent();
10411 if (typeof(parent.labelAlign) !='undefined') {
10412 return parent.labelAlign;
10419 getAutoCreate : function()
10421 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10427 if(this.inputType != 'hidden'){
10428 cfg.cls = 'form-group' //input-group
10434 type : this.inputType,
10435 value : this.value,
10436 cls : 'form-control',
10437 placeholder : this.placeholder || '',
10438 autocomplete : this.autocomplete || 'new-password'
10441 if(this.capture.length){
10442 input.capture = this.capture;
10445 if(this.accept.length){
10446 input.accept = this.accept + "/*";
10450 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10453 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10454 input.maxLength = this.maxLength;
10457 if (this.disabled) {
10458 input.disabled=true;
10461 if (this.readOnly) {
10462 input.readonly=true;
10466 input.name = this.name;
10470 input.cls += ' input-' + this.size;
10474 ['xs','sm','md','lg'].map(function(size){
10475 if (settings[size]) {
10476 cfg.cls += ' col-' + size + '-' + settings[size];
10480 var inputblock = input;
10484 cls: 'glyphicon form-control-feedback'
10487 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10490 cls : 'has-feedback',
10498 if (this.before || this.after) {
10501 cls : 'input-group',
10505 if (this.before && typeof(this.before) == 'string') {
10507 inputblock.cn.push({
10509 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10513 if (this.before && typeof(this.before) == 'object') {
10514 this.before = Roo.factory(this.before);
10516 inputblock.cn.push({
10518 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10519 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10523 inputblock.cn.push(input);
10525 if (this.after && typeof(this.after) == 'string') {
10526 inputblock.cn.push({
10528 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10532 if (this.after && typeof(this.after) == 'object') {
10533 this.after = Roo.factory(this.after);
10535 inputblock.cn.push({
10537 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10538 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10542 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10543 inputblock.cls += ' has-feedback';
10544 inputblock.cn.push(feedback);
10549 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10550 tooltip : 'This field is required'
10552 if (Roo.bootstrap.version == 4) {
10555 style : 'display-none'
10558 if (align ==='left' && this.fieldLabel.length) {
10560 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10567 cls : 'control-label col-form-label',
10568 html : this.fieldLabel
10579 var labelCfg = cfg.cn[1];
10580 var contentCfg = cfg.cn[2];
10582 if(this.indicatorpos == 'right'){
10587 cls : 'control-label col-form-label',
10591 html : this.fieldLabel
10605 labelCfg = cfg.cn[0];
10606 contentCfg = cfg.cn[1];
10610 if(this.labelWidth > 12){
10611 labelCfg.style = "width: " + this.labelWidth + 'px';
10614 if(this.labelWidth < 13 && this.labelmd == 0){
10615 this.labelmd = this.labelWidth;
10618 if(this.labellg > 0){
10619 labelCfg.cls += ' col-lg-' + this.labellg;
10620 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10623 if(this.labelmd > 0){
10624 labelCfg.cls += ' col-md-' + this.labelmd;
10625 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10628 if(this.labelsm > 0){
10629 labelCfg.cls += ' col-sm-' + this.labelsm;
10630 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10633 if(this.labelxs > 0){
10634 labelCfg.cls += ' col-xs-' + this.labelxs;
10635 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10639 } else if ( this.fieldLabel.length) {
10644 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10645 tooltip : 'This field is required'
10649 //cls : 'input-group-addon',
10650 html : this.fieldLabel
10658 if(this.indicatorpos == 'right'){
10663 //cls : 'input-group-addon',
10664 html : this.fieldLabel
10669 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10670 tooltip : 'This field is required'
10690 if (this.parentType === 'Navbar' && this.parent().bar) {
10691 cfg.cls += ' navbar-form';
10694 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10695 // on BS4 we do this only if not form
10696 cfg.cls += ' navbar-form';
10704 * return the real input element.
10706 inputEl: function ()
10708 return this.el.select('input.form-control',true).first();
10711 tooltipEl : function()
10713 return this.inputEl();
10716 indicatorEl : function()
10718 if (Roo.bootstrap.version == 4) {
10719 return false; // not enabled in v4 yet.
10722 var indicator = this.el.select('i.roo-required-indicator',true).first();
10732 setDisabled : function(v)
10734 var i = this.inputEl().dom;
10736 i.removeAttribute('disabled');
10740 i.setAttribute('disabled','true');
10742 initEvents : function()
10745 this.inputEl().on("keydown" , this.fireKey, this);
10746 this.inputEl().on("focus", this.onFocus, this);
10747 this.inputEl().on("blur", this.onBlur, this);
10749 this.inputEl().relayEvent('keyup', this);
10751 this.indicator = this.indicatorEl();
10753 if(this.indicator){
10754 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10757 // reference to original value for reset
10758 this.originalValue = this.getValue();
10759 //Roo.form.TextField.superclass.initEvents.call(this);
10760 if(this.validationEvent == 'keyup'){
10761 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10762 this.inputEl().on('keyup', this.filterValidation, this);
10764 else if(this.validationEvent !== false){
10765 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10768 if(this.selectOnFocus){
10769 this.on("focus", this.preFocus, this);
10772 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10773 this.inputEl().on("keypress", this.filterKeys, this);
10775 this.inputEl().relayEvent('keypress', this);
10778 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10779 this.el.on("click", this.autoSize, this);
10782 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10783 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10786 if (typeof(this.before) == 'object') {
10787 this.before.render(this.el.select('.roo-input-before',true).first());
10789 if (typeof(this.after) == 'object') {
10790 this.after.render(this.el.select('.roo-input-after',true).first());
10793 this.inputEl().on('change', this.onChange, this);
10796 filterValidation : function(e){
10797 if(!e.isNavKeyPress()){
10798 this.validationTask.delay(this.validationDelay);
10802 * Validates the field value
10803 * @return {Boolean} True if the value is valid, else false
10805 validate : function(){
10806 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10807 if(this.disabled || this.validateValue(this.getRawValue())){
10812 this.markInvalid();
10818 * Validates a value according to the field's validation rules and marks the field as invalid
10819 * if the validation fails
10820 * @param {Mixed} value The value to validate
10821 * @return {Boolean} True if the value is valid, else false
10823 validateValue : function(value)
10825 if(this.getVisibilityEl().hasClass('hidden')){
10829 if(value.length < 1) { // if it's blank
10830 if(this.allowBlank){
10836 if(value.length < this.minLength){
10839 if(value.length > this.maxLength){
10843 var vt = Roo.form.VTypes;
10844 if(!vt[this.vtype](value, this)){
10848 if(typeof this.validator == "function"){
10849 var msg = this.validator(value);
10853 if (typeof(msg) == 'string') {
10854 this.invalidText = msg;
10858 if(this.regex && !this.regex.test(value)){
10866 fireKey : function(e){
10867 //Roo.log('field ' + e.getKey());
10868 if(e.isNavKeyPress()){
10869 this.fireEvent("specialkey", this, e);
10872 focus : function (selectText){
10874 this.inputEl().focus();
10875 if(selectText === true){
10876 this.inputEl().dom.select();
10882 onFocus : function(){
10883 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10884 // this.el.addClass(this.focusClass);
10886 if(!this.hasFocus){
10887 this.hasFocus = true;
10888 this.startValue = this.getValue();
10889 this.fireEvent("focus", this);
10893 beforeBlur : Roo.emptyFn,
10897 onBlur : function(){
10899 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10900 //this.el.removeClass(this.focusClass);
10902 this.hasFocus = false;
10903 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10906 var v = this.getValue();
10907 if(String(v) !== String(this.startValue)){
10908 this.fireEvent('change', this, v, this.startValue);
10910 this.fireEvent("blur", this);
10913 onChange : function(e)
10915 var v = this.getValue();
10916 if(String(v) !== String(this.startValue)){
10917 this.fireEvent('change', this, v, this.startValue);
10923 * Resets the current field value to the originally loaded value and clears any validation messages
10925 reset : function(){
10926 this.setValue(this.originalValue);
10930 * Returns the name of the field
10931 * @return {Mixed} name The name field
10933 getName: function(){
10937 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10938 * @return {Mixed} value The field value
10940 getValue : function(){
10942 var v = this.inputEl().getValue();
10947 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10948 * @return {Mixed} value The field value
10950 getRawValue : function(){
10951 var v = this.inputEl().getValue();
10957 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10958 * @param {Mixed} value The value to set
10960 setRawValue : function(v){
10961 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10964 selectText : function(start, end){
10965 var v = this.getRawValue();
10967 start = start === undefined ? 0 : start;
10968 end = end === undefined ? v.length : end;
10969 var d = this.inputEl().dom;
10970 if(d.setSelectionRange){
10971 d.setSelectionRange(start, end);
10972 }else if(d.createTextRange){
10973 var range = d.createTextRange();
10974 range.moveStart("character", start);
10975 range.moveEnd("character", v.length-end);
10982 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10983 * @param {Mixed} value The value to set
10985 setValue : function(v){
10988 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10994 processValue : function(value){
10995 if(this.stripCharsRe){
10996 var newValue = value.replace(this.stripCharsRe, '');
10997 if(newValue !== value){
10998 this.setRawValue(newValue);
11005 preFocus : function(){
11007 if(this.selectOnFocus){
11008 this.inputEl().dom.select();
11011 filterKeys : function(e){
11012 var k = e.getKey();
11013 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11016 var c = e.getCharCode(), cc = String.fromCharCode(c);
11017 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11020 if(!this.maskRe.test(cc)){
11025 * Clear any invalid styles/messages for this field
11027 clearInvalid : function(){
11029 if(!this.el || this.preventMark){ // not rendered
11034 this.el.removeClass([this.invalidClass, 'is-invalid']);
11036 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11038 var feedback = this.el.select('.form-control-feedback', true).first();
11041 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11046 if(this.indicator){
11047 this.indicator.removeClass('visible');
11048 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11051 this.fireEvent('valid', this);
11055 * Mark this field as valid
11057 markValid : function()
11059 if(!this.el || this.preventMark){ // not rendered...
11063 this.el.removeClass([this.invalidClass, this.validClass]);
11064 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11066 var feedback = this.el.select('.form-control-feedback', true).first();
11069 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11072 if(this.indicator){
11073 this.indicator.removeClass('visible');
11074 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11082 if(this.allowBlank && !this.getRawValue().length){
11085 if (Roo.bootstrap.version == 3) {
11086 this.el.addClass(this.validClass);
11088 this.inputEl().addClass('is-valid');
11091 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11093 var feedback = this.el.select('.form-control-feedback', true).first();
11096 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11097 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11102 this.fireEvent('valid', this);
11106 * Mark this field as invalid
11107 * @param {String} msg The validation message
11109 markInvalid : function(msg)
11111 if(!this.el || this.preventMark){ // not rendered
11115 this.el.removeClass([this.invalidClass, this.validClass]);
11116 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11118 var feedback = this.el.select('.form-control-feedback', true).first();
11121 this.el.select('.form-control-feedback', true).first().removeClass(
11122 [this.invalidFeedbackClass, this.validFeedbackClass]);
11129 if(this.allowBlank && !this.getRawValue().length){
11133 if(this.indicator){
11134 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11135 this.indicator.addClass('visible');
11137 if (Roo.bootstrap.version == 3) {
11138 this.el.addClass(this.invalidClass);
11140 this.inputEl().addClass('is-invalid');
11145 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11147 var feedback = this.el.select('.form-control-feedback', true).first();
11150 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11152 if(this.getValue().length || this.forceFeedback){
11153 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11160 this.fireEvent('invalid', this, msg);
11163 SafariOnKeyDown : function(event)
11165 // this is a workaround for a password hang bug on chrome/ webkit.
11166 if (this.inputEl().dom.type != 'password') {
11170 var isSelectAll = false;
11172 if(this.inputEl().dom.selectionEnd > 0){
11173 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11175 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11176 event.preventDefault();
11181 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11183 event.preventDefault();
11184 // this is very hacky as keydown always get's upper case.
11186 var cc = String.fromCharCode(event.getCharCode());
11187 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11191 adjustWidth : function(tag, w){
11192 tag = tag.toLowerCase();
11193 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11194 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11195 if(tag == 'input'){
11198 if(tag == 'textarea'){
11201 }else if(Roo.isOpera){
11202 if(tag == 'input'){
11205 if(tag == 'textarea'){
11213 setFieldLabel : function(v)
11215 if(!this.rendered){
11219 if(this.indicatorEl()){
11220 var ar = this.el.select('label > span',true);
11222 if (ar.elements.length) {
11223 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11224 this.fieldLabel = v;
11228 var br = this.el.select('label',true);
11230 if(br.elements.length) {
11231 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11232 this.fieldLabel = v;
11236 Roo.log('Cannot Found any of label > span || label in input');
11240 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11241 this.fieldLabel = v;
11256 * @class Roo.bootstrap.TextArea
11257 * @extends Roo.bootstrap.Input
11258 * Bootstrap TextArea class
11259 * @cfg {Number} cols Specifies the visible width of a text area
11260 * @cfg {Number} rows Specifies the visible number of lines in a text area
11261 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11262 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11263 * @cfg {string} html text
11266 * Create a new TextArea
11267 * @param {Object} config The config object
11270 Roo.bootstrap.TextArea = function(config){
11271 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11275 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11285 getAutoCreate : function(){
11287 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11293 if(this.inputType != 'hidden'){
11294 cfg.cls = 'form-group' //input-group
11302 value : this.value || '',
11303 html: this.html || '',
11304 cls : 'form-control',
11305 placeholder : this.placeholder || ''
11309 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11310 input.maxLength = this.maxLength;
11314 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11318 input.cols = this.cols;
11321 if (this.readOnly) {
11322 input.readonly = true;
11326 input.name = this.name;
11330 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11334 ['xs','sm','md','lg'].map(function(size){
11335 if (settings[size]) {
11336 cfg.cls += ' col-' + size + '-' + settings[size];
11340 var inputblock = input;
11342 if(this.hasFeedback && !this.allowBlank){
11346 cls: 'glyphicon form-control-feedback'
11350 cls : 'has-feedback',
11359 if (this.before || this.after) {
11362 cls : 'input-group',
11366 inputblock.cn.push({
11368 cls : 'input-group-addon',
11373 inputblock.cn.push(input);
11375 if(this.hasFeedback && !this.allowBlank){
11376 inputblock.cls += ' has-feedback';
11377 inputblock.cn.push(feedback);
11381 inputblock.cn.push({
11383 cls : 'input-group-addon',
11390 if (align ==='left' && this.fieldLabel.length) {
11395 cls : 'control-label',
11396 html : this.fieldLabel
11407 if(this.labelWidth > 12){
11408 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11411 if(this.labelWidth < 13 && this.labelmd == 0){
11412 this.labelmd = this.labelWidth;
11415 if(this.labellg > 0){
11416 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11417 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11420 if(this.labelmd > 0){
11421 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11422 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11425 if(this.labelsm > 0){
11426 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11427 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11430 if(this.labelxs > 0){
11431 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11432 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11435 } else if ( this.fieldLabel.length) {
11440 //cls : 'input-group-addon',
11441 html : this.fieldLabel
11459 if (this.disabled) {
11460 input.disabled=true;
11467 * return the real textarea element.
11469 inputEl: function ()
11471 return this.el.select('textarea.form-control',true).first();
11475 * Clear any invalid styles/messages for this field
11477 clearInvalid : function()
11480 if(!this.el || this.preventMark){ // not rendered
11484 var label = this.el.select('label', true).first();
11485 var icon = this.el.select('i.fa-star', true).first();
11490 this.el.removeClass( this.validClass);
11491 this.inputEl().removeClass('is-invalid');
11493 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11495 var feedback = this.el.select('.form-control-feedback', true).first();
11498 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11503 this.fireEvent('valid', this);
11507 * Mark this field as valid
11509 markValid : function()
11511 if(!this.el || this.preventMark){ // not rendered
11515 this.el.removeClass([this.invalidClass, this.validClass]);
11516 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11518 var feedback = this.el.select('.form-control-feedback', true).first();
11521 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11524 if(this.disabled || this.allowBlank){
11528 var label = this.el.select('label', true).first();
11529 var icon = this.el.select('i.fa-star', true).first();
11534 if (Roo.bootstrap.version == 3) {
11535 this.el.addClass(this.validClass);
11537 this.inputEl().addClass('is-valid');
11541 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11543 var feedback = this.el.select('.form-control-feedback', true).first();
11546 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11547 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11552 this.fireEvent('valid', this);
11556 * Mark this field as invalid
11557 * @param {String} msg The validation message
11559 markInvalid : function(msg)
11561 if(!this.el || this.preventMark){ // not rendered
11565 this.el.removeClass([this.invalidClass, this.validClass]);
11566 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11568 var feedback = this.el.select('.form-control-feedback', true).first();
11571 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11574 if(this.disabled || this.allowBlank){
11578 var label = this.el.select('label', true).first();
11579 var icon = this.el.select('i.fa-star', true).first();
11581 if(!this.getValue().length && label && !icon){
11582 this.el.createChild({
11584 cls : 'text-danger fa fa-lg fa-star',
11585 tooltip : 'This field is required',
11586 style : 'margin-right:5px;'
11590 if (Roo.bootstrap.version == 3) {
11591 this.el.addClass(this.invalidClass);
11593 this.inputEl().addClass('is-invalid');
11596 // fixme ... this may be depricated need to test..
11597 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11599 var feedback = this.el.select('.form-control-feedback', true).first();
11602 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11604 if(this.getValue().length || this.forceFeedback){
11605 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11612 this.fireEvent('invalid', this, msg);
11620 * trigger field - base class for combo..
11625 * @class Roo.bootstrap.TriggerField
11626 * @extends Roo.bootstrap.Input
11627 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11628 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11629 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11630 * for which you can provide a custom implementation. For example:
11632 var trigger = new Roo.bootstrap.TriggerField();
11633 trigger.onTriggerClick = myTriggerFn;
11634 trigger.applyTo('my-field');
11637 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11638 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11639 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11640 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11641 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11644 * Create a new TriggerField.
11645 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11646 * to the base TextField)
11648 Roo.bootstrap.TriggerField = function(config){
11649 this.mimicing = false;
11650 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11653 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11655 * @cfg {String} triggerClass A CSS class to apply to the trigger
11658 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11663 * @cfg {Boolean} removable (true|false) special filter default false
11667 /** @cfg {Boolean} grow @hide */
11668 /** @cfg {Number} growMin @hide */
11669 /** @cfg {Number} growMax @hide */
11675 autoSize: Roo.emptyFn,
11679 deferHeight : true,
11682 actionMode : 'wrap',
11687 getAutoCreate : function(){
11689 var align = this.labelAlign || this.parentLabelAlign();
11694 cls: 'form-group' //input-group
11701 type : this.inputType,
11702 cls : 'form-control',
11703 autocomplete: 'new-password',
11704 placeholder : this.placeholder || ''
11708 input.name = this.name;
11711 input.cls += ' input-' + this.size;
11714 if (this.disabled) {
11715 input.disabled=true;
11718 var inputblock = input;
11720 if(this.hasFeedback && !this.allowBlank){
11724 cls: 'glyphicon form-control-feedback'
11727 if(this.removable && !this.editable ){
11729 cls : 'has-feedback',
11735 cls : 'roo-combo-removable-btn close'
11742 cls : 'has-feedback',
11751 if(this.removable && !this.editable ){
11753 cls : 'roo-removable',
11759 cls : 'roo-combo-removable-btn close'
11766 if (this.before || this.after) {
11769 cls : 'input-group',
11773 inputblock.cn.push({
11775 cls : 'input-group-addon input-group-prepend input-group-text',
11780 inputblock.cn.push(input);
11782 if(this.hasFeedback && !this.allowBlank){
11783 inputblock.cls += ' has-feedback';
11784 inputblock.cn.push(feedback);
11788 inputblock.cn.push({
11790 cls : 'input-group-addon input-group-append input-group-text',
11799 var ibwrap = inputblock;
11804 cls: 'roo-select2-choices',
11808 cls: 'roo-select2-search-field',
11820 cls: 'roo-select2-container input-group',
11825 cls: 'form-hidden-field'
11831 if(!this.multiple && this.showToggleBtn){
11837 if (this.caret != false) {
11840 cls: 'fa fa-' + this.caret
11847 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11849 Roo.bootstrap.version == 3 ? caret : '',
11852 cls: 'combobox-clear',
11866 combobox.cls += ' roo-select2-container-multi';
11870 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11871 tooltip : 'This field is required'
11873 if (Roo.bootstrap.version == 4) {
11876 style : 'display:none'
11881 if (align ==='left' && this.fieldLabel.length) {
11883 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11890 cls : 'control-label',
11891 html : this.fieldLabel
11903 var labelCfg = cfg.cn[1];
11904 var contentCfg = cfg.cn[2];
11906 if(this.indicatorpos == 'right'){
11911 cls : 'control-label',
11915 html : this.fieldLabel
11929 labelCfg = cfg.cn[0];
11930 contentCfg = cfg.cn[1];
11933 if(this.labelWidth > 12){
11934 labelCfg.style = "width: " + this.labelWidth + 'px';
11937 if(this.labelWidth < 13 && this.labelmd == 0){
11938 this.labelmd = this.labelWidth;
11941 if(this.labellg > 0){
11942 labelCfg.cls += ' col-lg-' + this.labellg;
11943 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11946 if(this.labelmd > 0){
11947 labelCfg.cls += ' col-md-' + this.labelmd;
11948 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11951 if(this.labelsm > 0){
11952 labelCfg.cls += ' col-sm-' + this.labelsm;
11953 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11956 if(this.labelxs > 0){
11957 labelCfg.cls += ' col-xs-' + this.labelxs;
11958 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11961 } else if ( this.fieldLabel.length) {
11962 // Roo.log(" label");
11967 //cls : 'input-group-addon',
11968 html : this.fieldLabel
11976 if(this.indicatorpos == 'right'){
11984 html : this.fieldLabel
11998 // Roo.log(" no label && no align");
12005 ['xs','sm','md','lg'].map(function(size){
12006 if (settings[size]) {
12007 cfg.cls += ' col-' + size + '-' + settings[size];
12018 onResize : function(w, h){
12019 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12020 // if(typeof w == 'number'){
12021 // var x = w - this.trigger.getWidth();
12022 // this.inputEl().setWidth(this.adjustWidth('input', x));
12023 // this.trigger.setStyle('left', x+'px');
12028 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12031 getResizeEl : function(){
12032 return this.inputEl();
12036 getPositionEl : function(){
12037 return this.inputEl();
12041 alignErrorIcon : function(){
12042 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12046 initEvents : function(){
12050 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12051 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12052 if(!this.multiple && this.showToggleBtn){
12053 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12054 if(this.hideTrigger){
12055 this.trigger.setDisplayed(false);
12057 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12061 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12064 if(this.removable && !this.editable && !this.tickable){
12065 var close = this.closeTriggerEl();
12068 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12069 close.on('click', this.removeBtnClick, this, close);
12073 //this.trigger.addClassOnOver('x-form-trigger-over');
12074 //this.trigger.addClassOnClick('x-form-trigger-click');
12077 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12081 closeTriggerEl : function()
12083 var close = this.el.select('.roo-combo-removable-btn', true).first();
12084 return close ? close : false;
12087 removeBtnClick : function(e, h, el)
12089 e.preventDefault();
12091 if(this.fireEvent("remove", this) !== false){
12093 this.fireEvent("afterremove", this)
12097 createList : function()
12099 this.list = Roo.get(document.body).createChild({
12100 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12101 cls: 'typeahead typeahead-long dropdown-menu',
12102 style: 'display:none'
12105 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12110 initTrigger : function(){
12115 onDestroy : function(){
12117 this.trigger.removeAllListeners();
12118 // this.trigger.remove();
12121 // this.wrap.remove();
12123 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12127 onFocus : function(){
12128 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12130 if(!this.mimicing){
12131 this.wrap.addClass('x-trigger-wrap-focus');
12132 this.mimicing = true;
12133 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12134 if(this.monitorTab){
12135 this.el.on("keydown", this.checkTab, this);
12142 checkTab : function(e){
12143 if(e.getKey() == e.TAB){
12144 this.triggerBlur();
12149 onBlur : function(){
12154 mimicBlur : function(e, t){
12156 if(!this.wrap.contains(t) && this.validateBlur()){
12157 this.triggerBlur();
12163 triggerBlur : function(){
12164 this.mimicing = false;
12165 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12166 if(this.monitorTab){
12167 this.el.un("keydown", this.checkTab, this);
12169 //this.wrap.removeClass('x-trigger-wrap-focus');
12170 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12174 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12175 validateBlur : function(e, t){
12180 onDisable : function(){
12181 this.inputEl().dom.disabled = true;
12182 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12184 // this.wrap.addClass('x-item-disabled');
12189 onEnable : function(){
12190 this.inputEl().dom.disabled = false;
12191 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12193 // this.el.removeClass('x-item-disabled');
12198 onShow : function(){
12199 var ae = this.getActionEl();
12202 ae.dom.style.display = '';
12203 ae.dom.style.visibility = 'visible';
12209 onHide : function(){
12210 var ae = this.getActionEl();
12211 ae.dom.style.display = 'none';
12215 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12216 * by an implementing function.
12218 * @param {EventObject} e
12220 onTriggerClick : Roo.emptyFn
12228 * @class Roo.bootstrap.CardUploader
12229 * @extends Roo.bootstrap.Button
12230 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12231 * @cfg {Number} errorTimeout default 3000
12232 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12233 * @cfg {Array} html The button text.
12237 * Create a new CardUploader
12238 * @param {Object} config The config object
12241 Roo.bootstrap.CardUploader = function(config){
12245 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12248 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12255 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12258 errorTimeout : 3000,
12262 fileCollection : false,
12265 getAutoCreate : function()
12269 cls :'form-group' ,
12274 //cls : 'input-group-addon',
12275 html : this.fieldLabel
12282 value : this.value,
12283 cls : 'd-none form-control'
12288 multiple : 'multiple',
12290 cls : 'd-none roo-card-upload-selector'
12294 cls : 'roo-card-uploader-button-container w-100 mb-2'
12297 cls : 'card-columns roo-card-uploader-container'
12307 getChildContainer : function() /// what children are added to.
12309 return this.containerEl;
12312 getButtonContainer : function() /// what children are added to.
12314 return this.el.select(".roo-card-uploader-button-container").first();
12317 initEvents : function()
12320 Roo.bootstrap.Input.prototype.initEvents.call(this);
12324 xns: Roo.bootstrap,
12327 container_method : 'getButtonContainer' ,
12328 html : this.html, // fix changable?
12331 'click' : function(btn, e) {
12340 this.urlAPI = (window.createObjectURL && window) ||
12341 (window.URL && URL.revokeObjectURL && URL) ||
12342 (window.webkitURL && webkitURL);
12347 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12349 this.selectorEl.on('change', this.onFileSelected, this);
12352 this.images.forEach(function(img) {
12355 this.images = false;
12357 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12363 onClick : function(e)
12365 e.preventDefault();
12367 this.selectorEl.dom.click();
12371 onFileSelected : function(e)
12373 e.preventDefault();
12375 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12379 Roo.each(this.selectorEl.dom.files, function(file){
12380 this.addFile(file);
12389 addFile : function(file)
12392 if(typeof(file) === 'string'){
12393 throw "Add file by name?"; // should not happen
12397 if(!file || !this.urlAPI){
12407 var url = _this.urlAPI.createObjectURL( file);
12410 id : Roo.bootstrap.CardUploader.ID--,
12411 is_uploaded : false,
12414 mimetype : file.type,
12421 addCard : function (data)
12423 // hidden input element?
12424 // if the file is not an image...
12425 //then we need to use something other that and header_image
12430 xns : Roo.bootstrap,
12431 xtype : 'CardFooter',
12434 xns : Roo.bootstrap,
12440 xns : Roo.bootstrap,
12442 html : String.format("<small>{0}</small>", data.title),
12443 cls : 'col-11 text-left',
12448 click : function() {
12449 this.downloadCard(data.id)
12455 xns : Roo.bootstrap,
12463 click : function() {
12464 t.removeCard(data.id)
12476 var cn = this.addxtype(
12479 xns : Roo.bootstrap,
12482 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12483 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12484 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12489 initEvents : function() {
12490 Roo.bootstrap.Card.prototype.initEvents.call(this);
12491 this.imgEl = this.el.select('.card-img-top').first();
12493 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12494 this.imgEl.set({ 'pointer' : 'cursor' });
12503 // dont' really need ot update items.
12504 // this.items.push(cn);
12505 this.fileCollection.add(cn);
12506 this.updateInput();
12509 removeCard : function(id)
12512 var card = this.fileCollection.get(id);
12513 card.data.is_deleted = 1;
12514 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12515 this.fileCollection.remove(card);
12516 //this.items = this.items.filter(function(e) { return e != card });
12517 // dont' really need ot update items.
12518 card.el.dom.parentNode.removeChild(card.el.dom);
12523 this.fileCollection.each(function(card) {
12524 card.el.dom.parentNode.removeChild(card.el.dom);
12526 this.fileCollection.clear();
12527 this.updateInput();
12530 updateInput : function()
12533 this.fileCollection.each(function(e) {
12537 this.inputEl().dom.value = JSON.stringify(data);
12544 Roo.bootstrap.CardUploader.ID = -1;/*
12546 * Ext JS Library 1.1.1
12547 * Copyright(c) 2006-2007, Ext JS, LLC.
12549 * Originally Released Under LGPL - original licence link has changed is not relivant.
12552 * <script type="text/javascript">
12557 * @class Roo.data.SortTypes
12559 * Defines the default sorting (casting?) comparison functions used when sorting data.
12561 Roo.data.SortTypes = {
12563 * Default sort that does nothing
12564 * @param {Mixed} s The value being converted
12565 * @return {Mixed} The comparison value
12567 none : function(s){
12572 * The regular expression used to strip tags
12576 stripTagsRE : /<\/?[^>]+>/gi,
12579 * Strips all HTML tags to sort on text only
12580 * @param {Mixed} s The value being converted
12581 * @return {String} The comparison value
12583 asText : function(s){
12584 return String(s).replace(this.stripTagsRE, "");
12588 * Strips all HTML tags to sort on text only - Case insensitive
12589 * @param {Mixed} s The value being converted
12590 * @return {String} The comparison value
12592 asUCText : function(s){
12593 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12597 * Case insensitive string
12598 * @param {Mixed} s The value being converted
12599 * @return {String} The comparison value
12601 asUCString : function(s) {
12602 return String(s).toUpperCase();
12607 * @param {Mixed} s The value being converted
12608 * @return {Number} The comparison value
12610 asDate : function(s) {
12614 if(s instanceof Date){
12615 return s.getTime();
12617 return Date.parse(String(s));
12622 * @param {Mixed} s The value being converted
12623 * @return {Float} The comparison value
12625 asFloat : function(s) {
12626 var val = parseFloat(String(s).replace(/,/g, ""));
12635 * @param {Mixed} s The value being converted
12636 * @return {Number} The comparison value
12638 asInt : function(s) {
12639 var val = parseInt(String(s).replace(/,/g, ""));
12647 * Ext JS Library 1.1.1
12648 * Copyright(c) 2006-2007, Ext JS, LLC.
12650 * Originally Released Under LGPL - original licence link has changed is not relivant.
12653 * <script type="text/javascript">
12657 * @class Roo.data.Record
12658 * Instances of this class encapsulate both record <em>definition</em> information, and record
12659 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12660 * to access Records cached in an {@link Roo.data.Store} object.<br>
12662 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12663 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12666 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12668 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12669 * {@link #create}. The parameters are the same.
12670 * @param {Array} data An associative Array of data values keyed by the field name.
12671 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12672 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12673 * not specified an integer id is generated.
12675 Roo.data.Record = function(data, id){
12676 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12681 * Generate a constructor for a specific record layout.
12682 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12683 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12684 * Each field definition object may contain the following properties: <ul>
12685 * <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,
12686 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12687 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12688 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12689 * is being used, then this is a string containing the javascript expression to reference the data relative to
12690 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12691 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12692 * this may be omitted.</p></li>
12693 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12694 * <ul><li>auto (Default, implies no conversion)</li>
12699 * <li>date</li></ul></p></li>
12700 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12701 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12702 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12703 * by the Reader into an object that will be stored in the Record. It is passed the
12704 * following parameters:<ul>
12705 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12707 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12709 * <br>usage:<br><pre><code>
12710 var TopicRecord = Roo.data.Record.create(
12711 {name: 'title', mapping: 'topic_title'},
12712 {name: 'author', mapping: 'username'},
12713 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12714 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12715 {name: 'lastPoster', mapping: 'user2'},
12716 {name: 'excerpt', mapping: 'post_text'}
12719 var myNewRecord = new TopicRecord({
12720 title: 'Do my job please',
12723 lastPost: new Date(),
12724 lastPoster: 'Animal',
12725 excerpt: 'No way dude!'
12727 myStore.add(myNewRecord);
12732 Roo.data.Record.create = function(o){
12733 var f = function(){
12734 f.superclass.constructor.apply(this, arguments);
12736 Roo.extend(f, Roo.data.Record);
12737 var p = f.prototype;
12738 p.fields = new Roo.util.MixedCollection(false, function(field){
12741 for(var i = 0, len = o.length; i < len; i++){
12742 p.fields.add(new Roo.data.Field(o[i]));
12744 f.getField = function(name){
12745 return p.fields.get(name);
12750 Roo.data.Record.AUTO_ID = 1000;
12751 Roo.data.Record.EDIT = 'edit';
12752 Roo.data.Record.REJECT = 'reject';
12753 Roo.data.Record.COMMIT = 'commit';
12755 Roo.data.Record.prototype = {
12757 * Readonly flag - true if this record has been modified.
12766 join : function(store){
12767 this.store = store;
12771 * Set the named field to the specified value.
12772 * @param {String} name The name of the field to set.
12773 * @param {Object} value The value to set the field to.
12775 set : function(name, value){
12776 if(this.data[name] == value){
12780 if(!this.modified){
12781 this.modified = {};
12783 if(typeof this.modified[name] == 'undefined'){
12784 this.modified[name] = this.data[name];
12786 this.data[name] = value;
12787 if(!this.editing && this.store){
12788 this.store.afterEdit(this);
12793 * Get the value of the named field.
12794 * @param {String} name The name of the field to get the value of.
12795 * @return {Object} The value of the field.
12797 get : function(name){
12798 return this.data[name];
12802 beginEdit : function(){
12803 this.editing = true;
12804 this.modified = {};
12808 cancelEdit : function(){
12809 this.editing = false;
12810 delete this.modified;
12814 endEdit : function(){
12815 this.editing = false;
12816 if(this.dirty && this.store){
12817 this.store.afterEdit(this);
12822 * Usually called by the {@link Roo.data.Store} which owns the Record.
12823 * Rejects all changes made to the Record since either creation, or the last commit operation.
12824 * Modified fields are reverted to their original values.
12826 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12827 * of reject operations.
12829 reject : function(){
12830 var m = this.modified;
12832 if(typeof m[n] != "function"){
12833 this.data[n] = m[n];
12836 this.dirty = false;
12837 delete this.modified;
12838 this.editing = false;
12840 this.store.afterReject(this);
12845 * Usually called by the {@link Roo.data.Store} which owns the Record.
12846 * Commits all changes made to the Record since either creation, or the last commit operation.
12848 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12849 * of commit operations.
12851 commit : function(){
12852 this.dirty = false;
12853 delete this.modified;
12854 this.editing = false;
12856 this.store.afterCommit(this);
12861 hasError : function(){
12862 return this.error != null;
12866 clearError : function(){
12871 * Creates a copy of this record.
12872 * @param {String} id (optional) A new record id if you don't want to use this record's id
12875 copy : function(newId) {
12876 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12880 * Ext JS Library 1.1.1
12881 * Copyright(c) 2006-2007, Ext JS, LLC.
12883 * Originally Released Under LGPL - original licence link has changed is not relivant.
12886 * <script type="text/javascript">
12892 * @class Roo.data.Store
12893 * @extends Roo.util.Observable
12894 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12895 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12897 * 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
12898 * has no knowledge of the format of the data returned by the Proxy.<br>
12900 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12901 * instances from the data object. These records are cached and made available through accessor functions.
12903 * Creates a new Store.
12904 * @param {Object} config A config object containing the objects needed for the Store to access data,
12905 * and read the data into Records.
12907 Roo.data.Store = function(config){
12908 this.data = new Roo.util.MixedCollection(false);
12909 this.data.getKey = function(o){
12912 this.baseParams = {};
12914 this.paramNames = {
12919 "multisort" : "_multisort"
12922 if(config && config.data){
12923 this.inlineData = config.data;
12924 delete config.data;
12927 Roo.apply(this, config);
12929 if(this.reader){ // reader passed
12930 this.reader = Roo.factory(this.reader, Roo.data);
12931 this.reader.xmodule = this.xmodule || false;
12932 if(!this.recordType){
12933 this.recordType = this.reader.recordType;
12935 if(this.reader.onMetaChange){
12936 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12940 if(this.recordType){
12941 this.fields = this.recordType.prototype.fields;
12943 this.modified = [];
12947 * @event datachanged
12948 * Fires when the data cache has changed, and a widget which is using this Store
12949 * as a Record cache should refresh its view.
12950 * @param {Store} this
12952 datachanged : true,
12954 * @event metachange
12955 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12956 * @param {Store} this
12957 * @param {Object} meta The JSON metadata
12962 * Fires when Records have been added to the Store
12963 * @param {Store} this
12964 * @param {Roo.data.Record[]} records The array of Records added
12965 * @param {Number} index The index at which the record(s) were added
12970 * Fires when a Record has been removed from the Store
12971 * @param {Store} this
12972 * @param {Roo.data.Record} record The Record that was removed
12973 * @param {Number} index The index at which the record was removed
12978 * Fires when a Record has been updated
12979 * @param {Store} this
12980 * @param {Roo.data.Record} record The Record that was updated
12981 * @param {String} operation The update operation being performed. Value may be one of:
12983 Roo.data.Record.EDIT
12984 Roo.data.Record.REJECT
12985 Roo.data.Record.COMMIT
12991 * Fires when the data cache has been cleared.
12992 * @param {Store} this
12996 * @event beforeload
12997 * Fires before a request is made for a new data object. If the beforeload handler returns false
12998 * the load action will be canceled.
12999 * @param {Store} this
13000 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13004 * @event beforeloadadd
13005 * Fires after a new set of Records has been loaded.
13006 * @param {Store} this
13007 * @param {Roo.data.Record[]} records The Records that were loaded
13008 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13010 beforeloadadd : true,
13013 * Fires after a new set of Records has been loaded, before they are added to the store.
13014 * @param {Store} this
13015 * @param {Roo.data.Record[]} records The Records that were loaded
13016 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13017 * @params {Object} return from reader
13021 * @event loadexception
13022 * Fires if an exception occurs in the Proxy during loading.
13023 * Called with the signature of the Proxy's "loadexception" event.
13024 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13027 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13028 * @param {Object} load options
13029 * @param {Object} jsonData from your request (normally this contains the Exception)
13031 loadexception : true
13035 this.proxy = Roo.factory(this.proxy, Roo.data);
13036 this.proxy.xmodule = this.xmodule || false;
13037 this.relayEvents(this.proxy, ["loadexception"]);
13039 this.sortToggle = {};
13040 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13042 Roo.data.Store.superclass.constructor.call(this);
13044 if(this.inlineData){
13045 this.loadData(this.inlineData);
13046 delete this.inlineData;
13050 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13052 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13053 * without a remote query - used by combo/forms at present.
13057 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13060 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13063 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13064 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13067 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13068 * on any HTTP request
13071 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13074 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13078 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13079 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13081 remoteSort : false,
13084 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13085 * loaded or when a record is removed. (defaults to false).
13087 pruneModifiedRecords : false,
13090 lastOptions : null,
13093 * Add Records to the Store and fires the add event.
13094 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13096 add : function(records){
13097 records = [].concat(records);
13098 for(var i = 0, len = records.length; i < len; i++){
13099 records[i].join(this);
13101 var index = this.data.length;
13102 this.data.addAll(records);
13103 this.fireEvent("add", this, records, index);
13107 * Remove a Record from the Store and fires the remove event.
13108 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13110 remove : function(record){
13111 var index = this.data.indexOf(record);
13112 this.data.removeAt(index);
13114 if(this.pruneModifiedRecords){
13115 this.modified.remove(record);
13117 this.fireEvent("remove", this, record, index);
13121 * Remove all Records from the Store and fires the clear event.
13123 removeAll : function(){
13125 if(this.pruneModifiedRecords){
13126 this.modified = [];
13128 this.fireEvent("clear", this);
13132 * Inserts Records to the Store at the given index and fires the add event.
13133 * @param {Number} index The start index at which to insert the passed Records.
13134 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13136 insert : function(index, records){
13137 records = [].concat(records);
13138 for(var i = 0, len = records.length; i < len; i++){
13139 this.data.insert(index, records[i]);
13140 records[i].join(this);
13142 this.fireEvent("add", this, records, index);
13146 * Get the index within the cache of the passed Record.
13147 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13148 * @return {Number} The index of the passed Record. Returns -1 if not found.
13150 indexOf : function(record){
13151 return this.data.indexOf(record);
13155 * Get the index within the cache of the Record with the passed id.
13156 * @param {String} id The id of the Record to find.
13157 * @return {Number} The index of the Record. Returns -1 if not found.
13159 indexOfId : function(id){
13160 return this.data.indexOfKey(id);
13164 * Get the Record with the specified id.
13165 * @param {String} id The id of the Record to find.
13166 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13168 getById : function(id){
13169 return this.data.key(id);
13173 * Get the Record at the specified index.
13174 * @param {Number} index The index of the Record to find.
13175 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13177 getAt : function(index){
13178 return this.data.itemAt(index);
13182 * Returns a range of Records between specified indices.
13183 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13184 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13185 * @return {Roo.data.Record[]} An array of Records
13187 getRange : function(start, end){
13188 return this.data.getRange(start, end);
13192 storeOptions : function(o){
13193 o = Roo.apply({}, o);
13196 this.lastOptions = o;
13200 * Loads the Record cache from the configured Proxy using the configured Reader.
13202 * If using remote paging, then the first load call must specify the <em>start</em>
13203 * and <em>limit</em> properties in the options.params property to establish the initial
13204 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13206 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13207 * and this call will return before the new data has been loaded. Perform any post-processing
13208 * in a callback function, or in a "load" event handler.</strong>
13210 * @param {Object} options An object containing properties which control loading options:<ul>
13211 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13212 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13213 * passed the following arguments:<ul>
13214 * <li>r : Roo.data.Record[]</li>
13215 * <li>options: Options object from the load call</li>
13216 * <li>success: Boolean success indicator</li></ul></li>
13217 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13218 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13221 load : function(options){
13222 options = options || {};
13223 if(this.fireEvent("beforeload", this, options) !== false){
13224 this.storeOptions(options);
13225 var p = Roo.apply(options.params || {}, this.baseParams);
13226 // if meta was not loaded from remote source.. try requesting it.
13227 if (!this.reader.metaFromRemote) {
13228 p._requestMeta = 1;
13230 if(this.sortInfo && this.remoteSort){
13231 var pn = this.paramNames;
13232 p[pn["sort"]] = this.sortInfo.field;
13233 p[pn["dir"]] = this.sortInfo.direction;
13235 if (this.multiSort) {
13236 var pn = this.paramNames;
13237 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13240 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13245 * Reloads the Record cache from the configured Proxy using the configured Reader and
13246 * the options from the last load operation performed.
13247 * @param {Object} options (optional) An object containing properties which may override the options
13248 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13249 * the most recently used options are reused).
13251 reload : function(options){
13252 this.load(Roo.applyIf(options||{}, this.lastOptions));
13256 // Called as a callback by the Reader during a load operation.
13257 loadRecords : function(o, options, success){
13258 if(!o || success === false){
13259 if(success !== false){
13260 this.fireEvent("load", this, [], options, o);
13262 if(options.callback){
13263 options.callback.call(options.scope || this, [], options, false);
13267 // if data returned failure - throw an exception.
13268 if (o.success === false) {
13269 // show a message if no listener is registered.
13270 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13271 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13273 // loadmask wil be hooked into this..
13274 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13277 var r = o.records, t = o.totalRecords || r.length;
13279 this.fireEvent("beforeloadadd", this, r, options, o);
13281 if(!options || options.add !== true){
13282 if(this.pruneModifiedRecords){
13283 this.modified = [];
13285 for(var i = 0, len = r.length; i < len; i++){
13289 this.data = this.snapshot;
13290 delete this.snapshot;
13293 this.data.addAll(r);
13294 this.totalLength = t;
13296 this.fireEvent("datachanged", this);
13298 this.totalLength = Math.max(t, this.data.length+r.length);
13302 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13304 var e = new Roo.data.Record({});
13306 e.set(this.parent.displayField, this.parent.emptyTitle);
13307 e.set(this.parent.valueField, '');
13312 this.fireEvent("load", this, r, options, o);
13313 if(options.callback){
13314 options.callback.call(options.scope || this, r, options, true);
13320 * Loads data from a passed data block. A Reader which understands the format of the data
13321 * must have been configured in the constructor.
13322 * @param {Object} data The data block from which to read the Records. The format of the data expected
13323 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13324 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13326 loadData : function(o, append){
13327 var r = this.reader.readRecords(o);
13328 this.loadRecords(r, {add: append}, true);
13332 * using 'cn' the nested child reader read the child array into it's child stores.
13333 * @param {Object} rec The record with a 'children array
13335 loadDataFromChildren : function(rec)
13337 this.loadData(this.reader.toLoadData(rec));
13342 * Gets the number of cached records.
13344 * <em>If using paging, this may not be the total size of the dataset. If the data object
13345 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13346 * the data set size</em>
13348 getCount : function(){
13349 return this.data.length || 0;
13353 * Gets the total number of records in the dataset as returned by the server.
13355 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13356 * the dataset size</em>
13358 getTotalCount : function(){
13359 return this.totalLength || 0;
13363 * Returns the sort state of the Store as an object with two properties:
13365 field {String} The name of the field by which the Records are sorted
13366 direction {String} The sort order, "ASC" or "DESC"
13369 getSortState : function(){
13370 return this.sortInfo;
13374 applySort : function(){
13375 if(this.sortInfo && !this.remoteSort){
13376 var s = this.sortInfo, f = s.field;
13377 var st = this.fields.get(f).sortType;
13378 var fn = function(r1, r2){
13379 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13380 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13382 this.data.sort(s.direction, fn);
13383 if(this.snapshot && this.snapshot != this.data){
13384 this.snapshot.sort(s.direction, fn);
13390 * Sets the default sort column and order to be used by the next load operation.
13391 * @param {String} fieldName The name of the field to sort by.
13392 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13394 setDefaultSort : function(field, dir){
13395 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13399 * Sort the Records.
13400 * If remote sorting is used, the sort is performed on the server, and the cache is
13401 * reloaded. If local sorting is used, the cache is sorted internally.
13402 * @param {String} fieldName The name of the field to sort by.
13403 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13405 sort : function(fieldName, dir){
13406 var f = this.fields.get(fieldName);
13408 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13410 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13411 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13416 this.sortToggle[f.name] = dir;
13417 this.sortInfo = {field: f.name, direction: dir};
13418 if(!this.remoteSort){
13420 this.fireEvent("datachanged", this);
13422 this.load(this.lastOptions);
13427 * Calls the specified function for each of the Records in the cache.
13428 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13429 * Returning <em>false</em> aborts and exits the iteration.
13430 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13432 each : function(fn, scope){
13433 this.data.each(fn, scope);
13437 * Gets all records modified since the last commit. Modified records are persisted across load operations
13438 * (e.g., during paging).
13439 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13441 getModifiedRecords : function(){
13442 return this.modified;
13446 createFilterFn : function(property, value, anyMatch){
13447 if(!value.exec){ // not a regex
13448 value = String(value);
13449 if(value.length == 0){
13452 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13454 return function(r){
13455 return value.test(r.data[property]);
13460 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13461 * @param {String} property A field on your records
13462 * @param {Number} start The record index to start at (defaults to 0)
13463 * @param {Number} end The last record index to include (defaults to length - 1)
13464 * @return {Number} The sum
13466 sum : function(property, start, end){
13467 var rs = this.data.items, v = 0;
13468 start = start || 0;
13469 end = (end || end === 0) ? end : rs.length-1;
13471 for(var i = start; i <= end; i++){
13472 v += (rs[i].data[property] || 0);
13478 * Filter the records by a specified property.
13479 * @param {String} field A field on your records
13480 * @param {String/RegExp} value Either a string that the field
13481 * should start with or a RegExp to test against the field
13482 * @param {Boolean} anyMatch True to match any part not just the beginning
13484 filter : function(property, value, anyMatch){
13485 var fn = this.createFilterFn(property, value, anyMatch);
13486 return fn ? this.filterBy(fn) : this.clearFilter();
13490 * Filter by a function. The specified function will be called with each
13491 * record in this data source. If the function returns true the record is included,
13492 * otherwise it is filtered.
13493 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13494 * @param {Object} scope (optional) The scope of the function (defaults to this)
13496 filterBy : function(fn, scope){
13497 this.snapshot = this.snapshot || this.data;
13498 this.data = this.queryBy(fn, scope||this);
13499 this.fireEvent("datachanged", this);
13503 * Query the records by a specified property.
13504 * @param {String} field A field on your records
13505 * @param {String/RegExp} value Either a string that the field
13506 * should start with or a RegExp to test against the field
13507 * @param {Boolean} anyMatch True to match any part not just the beginning
13508 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13510 query : function(property, value, anyMatch){
13511 var fn = this.createFilterFn(property, value, anyMatch);
13512 return fn ? this.queryBy(fn) : this.data.clone();
13516 * Query by a function. The specified function will be called with each
13517 * record in this data source. If the function returns true the record is included
13519 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13520 * @param {Object} scope (optional) The scope of the function (defaults to this)
13521 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13523 queryBy : function(fn, scope){
13524 var data = this.snapshot || this.data;
13525 return data.filterBy(fn, scope||this);
13529 * Collects unique values for a particular dataIndex from this store.
13530 * @param {String} dataIndex The property to collect
13531 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13532 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13533 * @return {Array} An array of the unique values
13535 collect : function(dataIndex, allowNull, bypassFilter){
13536 var d = (bypassFilter === true && this.snapshot) ?
13537 this.snapshot.items : this.data.items;
13538 var v, sv, r = [], l = {};
13539 for(var i = 0, len = d.length; i < len; i++){
13540 v = d[i].data[dataIndex];
13542 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13551 * Revert to a view of the Record cache with no filtering applied.
13552 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13554 clearFilter : function(suppressEvent){
13555 if(this.snapshot && this.snapshot != this.data){
13556 this.data = this.snapshot;
13557 delete this.snapshot;
13558 if(suppressEvent !== true){
13559 this.fireEvent("datachanged", this);
13565 afterEdit : function(record){
13566 if(this.modified.indexOf(record) == -1){
13567 this.modified.push(record);
13569 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13573 afterReject : function(record){
13574 this.modified.remove(record);
13575 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13579 afterCommit : function(record){
13580 this.modified.remove(record);
13581 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13585 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13586 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13588 commitChanges : function(){
13589 var m = this.modified.slice(0);
13590 this.modified = [];
13591 for(var i = 0, len = m.length; i < len; i++){
13597 * Cancel outstanding changes on all changed records.
13599 rejectChanges : function(){
13600 var m = this.modified.slice(0);
13601 this.modified = [];
13602 for(var i = 0, len = m.length; i < len; i++){
13607 onMetaChange : function(meta, rtype, o){
13608 this.recordType = rtype;
13609 this.fields = rtype.prototype.fields;
13610 delete this.snapshot;
13611 this.sortInfo = meta.sortInfo || this.sortInfo;
13612 this.modified = [];
13613 this.fireEvent('metachange', this, this.reader.meta);
13616 moveIndex : function(data, type)
13618 var index = this.indexOf(data);
13620 var newIndex = index + type;
13624 this.insert(newIndex, data);
13629 * Ext JS Library 1.1.1
13630 * Copyright(c) 2006-2007, Ext JS, LLC.
13632 * Originally Released Under LGPL - original licence link has changed is not relivant.
13635 * <script type="text/javascript">
13639 * @class Roo.data.SimpleStore
13640 * @extends Roo.data.Store
13641 * Small helper class to make creating Stores from Array data easier.
13642 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13643 * @cfg {Array} fields An array of field definition objects, or field name strings.
13644 * @cfg {Object} an existing reader (eg. copied from another store)
13645 * @cfg {Array} data The multi-dimensional array of data
13647 * @param {Object} config
13649 Roo.data.SimpleStore = function(config)
13651 Roo.data.SimpleStore.superclass.constructor.call(this, {
13653 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13656 Roo.data.Record.create(config.fields)
13658 proxy : new Roo.data.MemoryProxy(config.data)
13662 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13664 * Ext JS Library 1.1.1
13665 * Copyright(c) 2006-2007, Ext JS, LLC.
13667 * Originally Released Under LGPL - original licence link has changed is not relivant.
13670 * <script type="text/javascript">
13675 * @extends Roo.data.Store
13676 * @class Roo.data.JsonStore
13677 * Small helper class to make creating Stores for JSON data easier. <br/>
13679 var store = new Roo.data.JsonStore({
13680 url: 'get-images.php',
13682 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13685 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13686 * JsonReader and HttpProxy (unless inline data is provided).</b>
13687 * @cfg {Array} fields An array of field definition objects, or field name strings.
13689 * @param {Object} config
13691 Roo.data.JsonStore = function(c){
13692 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13693 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13694 reader: new Roo.data.JsonReader(c, c.fields)
13697 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13699 * Ext JS Library 1.1.1
13700 * Copyright(c) 2006-2007, Ext JS, LLC.
13702 * Originally Released Under LGPL - original licence link has changed is not relivant.
13705 * <script type="text/javascript">
13709 Roo.data.Field = function(config){
13710 if(typeof config == "string"){
13711 config = {name: config};
13713 Roo.apply(this, config);
13716 this.type = "auto";
13719 var st = Roo.data.SortTypes;
13720 // named sortTypes are supported, here we look them up
13721 if(typeof this.sortType == "string"){
13722 this.sortType = st[this.sortType];
13725 // set default sortType for strings and dates
13726 if(!this.sortType){
13729 this.sortType = st.asUCString;
13732 this.sortType = st.asDate;
13735 this.sortType = st.none;
13740 var stripRe = /[\$,%]/g;
13742 // prebuilt conversion function for this field, instead of
13743 // switching every time we're reading a value
13745 var cv, dateFormat = this.dateFormat;
13750 cv = function(v){ return v; };
13753 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13757 return v !== undefined && v !== null && v !== '' ?
13758 parseInt(String(v).replace(stripRe, ""), 10) : '';
13763 return v !== undefined && v !== null && v !== '' ?
13764 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13769 cv = function(v){ return v === true || v === "true" || v == 1; };
13776 if(v instanceof Date){
13780 if(dateFormat == "timestamp"){
13781 return new Date(v*1000);
13783 return Date.parseDate(v, dateFormat);
13785 var parsed = Date.parse(v);
13786 return parsed ? new Date(parsed) : null;
13795 Roo.data.Field.prototype = {
13803 * Ext JS Library 1.1.1
13804 * Copyright(c) 2006-2007, Ext JS, LLC.
13806 * Originally Released Under LGPL - original licence link has changed is not relivant.
13809 * <script type="text/javascript">
13812 // Base class for reading structured data from a data source. This class is intended to be
13813 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13816 * @class Roo.data.DataReader
13817 * Base class for reading structured data from a data source. This class is intended to be
13818 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13821 Roo.data.DataReader = function(meta, recordType){
13825 this.recordType = recordType instanceof Array ?
13826 Roo.data.Record.create(recordType) : recordType;
13829 Roo.data.DataReader.prototype = {
13832 readerType : 'Data',
13834 * Create an empty record
13835 * @param {Object} data (optional) - overlay some values
13836 * @return {Roo.data.Record} record created.
13838 newRow : function(d) {
13840 this.recordType.prototype.fields.each(function(c) {
13842 case 'int' : da[c.name] = 0; break;
13843 case 'date' : da[c.name] = new Date(); break;
13844 case 'float' : da[c.name] = 0.0; break;
13845 case 'boolean' : da[c.name] = false; break;
13846 default : da[c.name] = ""; break;
13850 return new this.recordType(Roo.apply(da, d));
13856 * Ext JS Library 1.1.1
13857 * Copyright(c) 2006-2007, Ext JS, LLC.
13859 * Originally Released Under LGPL - original licence link has changed is not relivant.
13862 * <script type="text/javascript">
13866 * @class Roo.data.DataProxy
13867 * @extends Roo.data.Observable
13868 * This class is an abstract base class for implementations which provide retrieval of
13869 * unformatted data objects.<br>
13871 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13872 * (of the appropriate type which knows how to parse the data object) to provide a block of
13873 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13875 * Custom implementations must implement the load method as described in
13876 * {@link Roo.data.HttpProxy#load}.
13878 Roo.data.DataProxy = function(){
13881 * @event beforeload
13882 * Fires before a network request is made to retrieve a data object.
13883 * @param {Object} This DataProxy object.
13884 * @param {Object} params The params parameter to the load function.
13889 * Fires before the load method's callback is called.
13890 * @param {Object} This DataProxy object.
13891 * @param {Object} o The data object.
13892 * @param {Object} arg The callback argument object passed to the load function.
13896 * @event loadexception
13897 * Fires if an Exception occurs during data retrieval.
13898 * @param {Object} This DataProxy object.
13899 * @param {Object} o The data object.
13900 * @param {Object} arg The callback argument object passed to the load function.
13901 * @param {Object} e The Exception.
13903 loadexception : true
13905 Roo.data.DataProxy.superclass.constructor.call(this);
13908 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13911 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13915 * Ext JS Library 1.1.1
13916 * Copyright(c) 2006-2007, Ext JS, LLC.
13918 * Originally Released Under LGPL - original licence link has changed is not relivant.
13921 * <script type="text/javascript">
13924 * @class Roo.data.MemoryProxy
13925 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13926 * to the Reader when its load method is called.
13928 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13930 Roo.data.MemoryProxy = function(data){
13934 Roo.data.MemoryProxy.superclass.constructor.call(this);
13938 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13941 * Load data from the requested source (in this case an in-memory
13942 * data object passed to the constructor), read the data object into
13943 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13944 * process that block using the passed callback.
13945 * @param {Object} params This parameter is not used by the MemoryProxy class.
13946 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13947 * object into a block of Roo.data.Records.
13948 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13949 * The function must be passed <ul>
13950 * <li>The Record block object</li>
13951 * <li>The "arg" argument from the load function</li>
13952 * <li>A boolean success indicator</li>
13954 * @param {Object} scope The scope in which to call the callback
13955 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13957 load : function(params, reader, callback, scope, arg){
13958 params = params || {};
13961 result = reader.readRecords(params.data ? params.data :this.data);
13963 this.fireEvent("loadexception", this, arg, null, e);
13964 callback.call(scope, null, arg, false);
13967 callback.call(scope, result, arg, true);
13971 update : function(params, records){
13976 * Ext JS Library 1.1.1
13977 * Copyright(c) 2006-2007, Ext JS, LLC.
13979 * Originally Released Under LGPL - original licence link has changed is not relivant.
13982 * <script type="text/javascript">
13985 * @class Roo.data.HttpProxy
13986 * @extends Roo.data.DataProxy
13987 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13988 * configured to reference a certain URL.<br><br>
13990 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13991 * from which the running page was served.<br><br>
13993 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13995 * Be aware that to enable the browser to parse an XML document, the server must set
13996 * the Content-Type header in the HTTP response to "text/xml".
13998 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13999 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14000 * will be used to make the request.
14002 Roo.data.HttpProxy = function(conn){
14003 Roo.data.HttpProxy.superclass.constructor.call(this);
14004 // is conn a conn config or a real conn?
14006 this.useAjax = !conn || !conn.events;
14010 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14011 // thse are take from connection...
14014 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14017 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14018 * extra parameters to each request made by this object. (defaults to undefined)
14021 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14022 * to each request made by this object. (defaults to undefined)
14025 * @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)
14028 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14031 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14037 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14041 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14042 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14043 * a finer-grained basis than the DataProxy events.
14045 getConnection : function(){
14046 return this.useAjax ? Roo.Ajax : this.conn;
14050 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14051 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14052 * process that block using the passed callback.
14053 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14054 * for the request to the remote server.
14055 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14056 * object into a block of Roo.data.Records.
14057 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14058 * The function must be passed <ul>
14059 * <li>The Record block object</li>
14060 * <li>The "arg" argument from the load function</li>
14061 * <li>A boolean success indicator</li>
14063 * @param {Object} scope The scope in which to call the callback
14064 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14066 load : function(params, reader, callback, scope, arg){
14067 if(this.fireEvent("beforeload", this, params) !== false){
14069 params : params || {},
14071 callback : callback,
14076 callback : this.loadResponse,
14080 Roo.applyIf(o, this.conn);
14081 if(this.activeRequest){
14082 Roo.Ajax.abort(this.activeRequest);
14084 this.activeRequest = Roo.Ajax.request(o);
14086 this.conn.request(o);
14089 callback.call(scope||this, null, arg, false);
14094 loadResponse : function(o, success, response){
14095 delete this.activeRequest;
14097 this.fireEvent("loadexception", this, o, response);
14098 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14103 result = o.reader.read(response);
14105 this.fireEvent("loadexception", this, o, response, e);
14106 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14110 this.fireEvent("load", this, o, o.request.arg);
14111 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14115 update : function(dataSet){
14120 updateResponse : function(dataSet){
14125 * Ext JS Library 1.1.1
14126 * Copyright(c) 2006-2007, Ext JS, LLC.
14128 * Originally Released Under LGPL - original licence link has changed is not relivant.
14131 * <script type="text/javascript">
14135 * @class Roo.data.ScriptTagProxy
14136 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14137 * other than the originating domain of the running page.<br><br>
14139 * <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
14140 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14142 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14143 * source code that is used as the source inside a <script> tag.<br><br>
14145 * In order for the browser to process the returned data, the server must wrap the data object
14146 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14147 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14148 * depending on whether the callback name was passed:
14151 boolean scriptTag = false;
14152 String cb = request.getParameter("callback");
14155 response.setContentType("text/javascript");
14157 response.setContentType("application/x-json");
14159 Writer out = response.getWriter();
14161 out.write(cb + "(");
14163 out.print(dataBlock.toJsonString());
14170 * @param {Object} config A configuration object.
14172 Roo.data.ScriptTagProxy = function(config){
14173 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14174 Roo.apply(this, config);
14175 this.head = document.getElementsByTagName("head")[0];
14178 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14180 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14182 * @cfg {String} url The URL from which to request the data object.
14185 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14189 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14190 * the server the name of the callback function set up by the load call to process the returned data object.
14191 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14192 * javascript output which calls this named function passing the data object as its only parameter.
14194 callbackParam : "callback",
14196 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14197 * name to the request.
14202 * Load data from the configured URL, read the data object into
14203 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14204 * process that block using the passed callback.
14205 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14206 * for the request to the remote server.
14207 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14208 * object into a block of Roo.data.Records.
14209 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14210 * The function must be passed <ul>
14211 * <li>The Record block object</li>
14212 * <li>The "arg" argument from the load function</li>
14213 * <li>A boolean success indicator</li>
14215 * @param {Object} scope The scope in which to call the callback
14216 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14218 load : function(params, reader, callback, scope, arg){
14219 if(this.fireEvent("beforeload", this, params) !== false){
14221 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14223 var url = this.url;
14224 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14226 url += "&_dc=" + (new Date().getTime());
14228 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14231 cb : "stcCallback"+transId,
14232 scriptId : "stcScript"+transId,
14236 callback : callback,
14242 window[trans.cb] = function(o){
14243 conn.handleResponse(o, trans);
14246 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14248 if(this.autoAbort !== false){
14252 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14254 var script = document.createElement("script");
14255 script.setAttribute("src", url);
14256 script.setAttribute("type", "text/javascript");
14257 script.setAttribute("id", trans.scriptId);
14258 this.head.appendChild(script);
14260 this.trans = trans;
14262 callback.call(scope||this, null, arg, false);
14267 isLoading : function(){
14268 return this.trans ? true : false;
14272 * Abort the current server request.
14274 abort : function(){
14275 if(this.isLoading()){
14276 this.destroyTrans(this.trans);
14281 destroyTrans : function(trans, isLoaded){
14282 this.head.removeChild(document.getElementById(trans.scriptId));
14283 clearTimeout(trans.timeoutId);
14285 window[trans.cb] = undefined;
14287 delete window[trans.cb];
14290 // if hasn't been loaded, wait for load to remove it to prevent script error
14291 window[trans.cb] = function(){
14292 window[trans.cb] = undefined;
14294 delete window[trans.cb];
14301 handleResponse : function(o, trans){
14302 this.trans = false;
14303 this.destroyTrans(trans, true);
14306 result = trans.reader.readRecords(o);
14308 this.fireEvent("loadexception", this, o, trans.arg, e);
14309 trans.callback.call(trans.scope||window, null, trans.arg, false);
14312 this.fireEvent("load", this, o, trans.arg);
14313 trans.callback.call(trans.scope||window, result, trans.arg, true);
14317 handleFailure : function(trans){
14318 this.trans = false;
14319 this.destroyTrans(trans, false);
14320 this.fireEvent("loadexception", this, null, trans.arg);
14321 trans.callback.call(trans.scope||window, null, trans.arg, false);
14325 * Ext JS Library 1.1.1
14326 * Copyright(c) 2006-2007, Ext JS, LLC.
14328 * Originally Released Under LGPL - original licence link has changed is not relivant.
14331 * <script type="text/javascript">
14335 * @class Roo.data.JsonReader
14336 * @extends Roo.data.DataReader
14337 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14338 * based on mappings in a provided Roo.data.Record constructor.
14340 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14341 * in the reply previously.
14346 var RecordDef = Roo.data.Record.create([
14347 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14348 {name: 'occupation'} // This field will use "occupation" as the mapping.
14350 var myReader = new Roo.data.JsonReader({
14351 totalProperty: "results", // The property which contains the total dataset size (optional)
14352 root: "rows", // The property which contains an Array of row objects
14353 id: "id" // The property within each row object that provides an ID for the record (optional)
14357 * This would consume a JSON file like this:
14359 { 'results': 2, 'rows': [
14360 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14361 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14364 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14365 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14366 * paged from the remote server.
14367 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14368 * @cfg {String} root name of the property which contains the Array of row objects.
14369 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14370 * @cfg {Array} fields Array of field definition objects
14372 * Create a new JsonReader
14373 * @param {Object} meta Metadata configuration options
14374 * @param {Object} recordType Either an Array of field definition objects,
14375 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14377 Roo.data.JsonReader = function(meta, recordType){
14380 // set some defaults:
14381 Roo.applyIf(meta, {
14382 totalProperty: 'total',
14383 successProperty : 'success',
14388 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14390 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14392 readerType : 'Json',
14395 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14396 * Used by Store query builder to append _requestMeta to params.
14399 metaFromRemote : false,
14401 * This method is only used by a DataProxy which has retrieved data from a remote server.
14402 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14403 * @return {Object} data A data block which is used by an Roo.data.Store object as
14404 * a cache of Roo.data.Records.
14406 read : function(response){
14407 var json = response.responseText;
14409 var o = /* eval:var:o */ eval("("+json+")");
14411 throw {message: "JsonReader.read: Json object not found"};
14417 this.metaFromRemote = true;
14418 this.meta = o.metaData;
14419 this.recordType = Roo.data.Record.create(o.metaData.fields);
14420 this.onMetaChange(this.meta, this.recordType, o);
14422 return this.readRecords(o);
14425 // private function a store will implement
14426 onMetaChange : function(meta, recordType, o){
14433 simpleAccess: function(obj, subsc) {
14440 getJsonAccessor: function(){
14442 return function(expr) {
14444 return(re.test(expr))
14445 ? new Function("obj", "return obj." + expr)
14450 return Roo.emptyFn;
14455 * Create a data block containing Roo.data.Records from an XML document.
14456 * @param {Object} o An object which contains an Array of row objects in the property specified
14457 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14458 * which contains the total size of the dataset.
14459 * @return {Object} data A data block which is used by an Roo.data.Store object as
14460 * a cache of Roo.data.Records.
14462 readRecords : function(o){
14464 * After any data loads, the raw JSON data is available for further custom processing.
14468 var s = this.meta, Record = this.recordType,
14469 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14471 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14473 if(s.totalProperty) {
14474 this.getTotal = this.getJsonAccessor(s.totalProperty);
14476 if(s.successProperty) {
14477 this.getSuccess = this.getJsonAccessor(s.successProperty);
14479 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14481 var g = this.getJsonAccessor(s.id);
14482 this.getId = function(rec) {
14484 return (r === undefined || r === "") ? null : r;
14487 this.getId = function(){return null;};
14490 for(var jj = 0; jj < fl; jj++){
14492 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14493 this.ef[jj] = this.getJsonAccessor(map);
14497 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14498 if(s.totalProperty){
14499 var vt = parseInt(this.getTotal(o), 10);
14504 if(s.successProperty){
14505 var vs = this.getSuccess(o);
14506 if(vs === false || vs === 'false'){
14511 for(var i = 0; i < c; i++){
14514 var id = this.getId(n);
14515 for(var j = 0; j < fl; j++){
14517 var v = this.ef[j](n);
14519 Roo.log('missing convert for ' + f.name);
14523 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14525 var record = new Record(values, id);
14527 records[i] = record;
14533 totalRecords : totalRecords
14536 // used when loading children.. @see loadDataFromChildren
14537 toLoadData: function(rec)
14539 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14540 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14541 return { data : data, total : data.length };
14546 * Ext JS Library 1.1.1
14547 * Copyright(c) 2006-2007, Ext JS, LLC.
14549 * Originally Released Under LGPL - original licence link has changed is not relivant.
14552 * <script type="text/javascript">
14556 * @class Roo.data.ArrayReader
14557 * @extends Roo.data.DataReader
14558 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14559 * Each element of that Array represents a row of data fields. The
14560 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14561 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14565 var RecordDef = Roo.data.Record.create([
14566 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14567 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14569 var myReader = new Roo.data.ArrayReader({
14570 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14574 * This would consume an Array like this:
14576 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14580 * Create a new JsonReader
14581 * @param {Object} meta Metadata configuration options.
14582 * @param {Object|Array} recordType Either an Array of field definition objects
14584 * @cfg {Array} fields Array of field definition objects
14585 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14586 * as specified to {@link Roo.data.Record#create},
14587 * or an {@link Roo.data.Record} object
14590 * created using {@link Roo.data.Record#create}.
14592 Roo.data.ArrayReader = function(meta, recordType)
14594 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14597 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14600 * Create a data block containing Roo.data.Records from an XML document.
14601 * @param {Object} o An Array of row objects which represents the dataset.
14602 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14603 * a cache of Roo.data.Records.
14605 readRecords : function(o)
14607 var sid = this.meta ? this.meta.id : null;
14608 var recordType = this.recordType, fields = recordType.prototype.fields;
14611 for(var i = 0; i < root.length; i++){
14614 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14615 for(var j = 0, jlen = fields.length; j < jlen; j++){
14616 var f = fields.items[j];
14617 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14618 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14620 values[f.name] = v;
14622 var record = new recordType(values, id);
14624 records[records.length] = record;
14628 totalRecords : records.length
14631 // used when loading children.. @see loadDataFromChildren
14632 toLoadData: function(rec)
14634 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14635 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14646 * @class Roo.bootstrap.ComboBox
14647 * @extends Roo.bootstrap.TriggerField
14648 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14649 * @cfg {Boolean} append (true|false) default false
14650 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14651 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14652 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14653 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14654 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14655 * @cfg {Boolean} animate default true
14656 * @cfg {Boolean} emptyResultText only for touch device
14657 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14658 * @cfg {String} emptyTitle default ''
14660 * Create a new ComboBox.
14661 * @param {Object} config Configuration options
14663 Roo.bootstrap.ComboBox = function(config){
14664 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14668 * Fires when the dropdown list is expanded
14669 * @param {Roo.bootstrap.ComboBox} combo This combo box
14674 * Fires when the dropdown list is collapsed
14675 * @param {Roo.bootstrap.ComboBox} combo This combo box
14679 * @event beforeselect
14680 * Fires before a list item is selected. Return false to cancel the selection.
14681 * @param {Roo.bootstrap.ComboBox} combo This combo box
14682 * @param {Roo.data.Record} record The data record returned from the underlying store
14683 * @param {Number} index The index of the selected item in the dropdown list
14685 'beforeselect' : true,
14688 * Fires when a list item is selected
14689 * @param {Roo.bootstrap.ComboBox} combo This combo box
14690 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14691 * @param {Number} index The index of the selected item in the dropdown list
14695 * @event beforequery
14696 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14697 * The event object passed has these properties:
14698 * @param {Roo.bootstrap.ComboBox} combo This combo box
14699 * @param {String} query The query
14700 * @param {Boolean} forceAll true to force "all" query
14701 * @param {Boolean} cancel true to cancel the query
14702 * @param {Object} e The query event object
14704 'beforequery': true,
14707 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14708 * @param {Roo.bootstrap.ComboBox} combo This combo box
14713 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14714 * @param {Roo.bootstrap.ComboBox} combo This combo box
14715 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14720 * Fires when the remove value from the combobox array
14721 * @param {Roo.bootstrap.ComboBox} combo This combo box
14725 * @event afterremove
14726 * Fires when the remove value from the combobox array
14727 * @param {Roo.bootstrap.ComboBox} combo This combo box
14729 'afterremove' : true,
14731 * @event specialfilter
14732 * Fires when specialfilter
14733 * @param {Roo.bootstrap.ComboBox} combo This combo box
14735 'specialfilter' : true,
14738 * Fires when tick the element
14739 * @param {Roo.bootstrap.ComboBox} combo This combo box
14743 * @event touchviewdisplay
14744 * Fires when touch view require special display (default is using displayField)
14745 * @param {Roo.bootstrap.ComboBox} combo This combo box
14746 * @param {Object} cfg set html .
14748 'touchviewdisplay' : true
14753 this.tickItems = [];
14755 this.selectedIndex = -1;
14756 if(this.mode == 'local'){
14757 if(config.queryDelay === undefined){
14758 this.queryDelay = 10;
14760 if(config.minChars === undefined){
14766 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14769 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14770 * rendering into an Roo.Editor, defaults to false)
14773 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14774 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14777 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14780 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14781 * the dropdown list (defaults to undefined, with no header element)
14785 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14789 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14791 listWidth: undefined,
14793 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14794 * mode = 'remote' or 'text' if mode = 'local')
14796 displayField: undefined,
14799 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14800 * mode = 'remote' or 'value' if mode = 'local').
14801 * Note: use of a valueField requires the user make a selection
14802 * in order for a value to be mapped.
14804 valueField: undefined,
14806 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14811 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14812 * field's data value (defaults to the underlying DOM element's name)
14814 hiddenName: undefined,
14816 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14820 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14822 selectedClass: 'active',
14825 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14829 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14830 * anchor positions (defaults to 'tl-bl')
14832 listAlign: 'tl-bl?',
14834 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14838 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14839 * query specified by the allQuery config option (defaults to 'query')
14841 triggerAction: 'query',
14843 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14844 * (defaults to 4, does not apply if editable = false)
14848 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14849 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14853 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14854 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14858 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14859 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14863 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14864 * when editable = true (defaults to false)
14866 selectOnFocus:false,
14868 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14870 queryParam: 'query',
14872 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14873 * when mode = 'remote' (defaults to 'Loading...')
14875 loadingText: 'Loading...',
14877 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14881 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14885 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14886 * traditional select (defaults to true)
14890 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14894 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14898 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14899 * listWidth has a higher value)
14903 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14904 * allow the user to set arbitrary text into the field (defaults to false)
14906 forceSelection:false,
14908 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14909 * if typeAhead = true (defaults to 250)
14911 typeAheadDelay : 250,
14913 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14914 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14916 valueNotFoundText : undefined,
14918 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14920 blockFocus : false,
14923 * @cfg {Boolean} disableClear Disable showing of clear button.
14925 disableClear : false,
14927 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14929 alwaysQuery : false,
14932 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14937 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14939 invalidClass : "has-warning",
14942 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14944 validClass : "has-success",
14947 * @cfg {Boolean} specialFilter (true|false) special filter default false
14949 specialFilter : false,
14952 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14954 mobileTouchView : true,
14957 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14959 useNativeIOS : false,
14962 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14964 mobile_restrict_height : false,
14966 ios_options : false,
14978 btnPosition : 'right',
14979 triggerList : true,
14980 showToggleBtn : true,
14982 emptyResultText: 'Empty',
14983 triggerText : 'Select',
14986 // element that contains real text value.. (when hidden is used..)
14988 getAutoCreate : function()
14993 * Render classic select for iso
14996 if(Roo.isIOS && this.useNativeIOS){
14997 cfg = this.getAutoCreateNativeIOS();
15005 if(Roo.isTouch && this.mobileTouchView){
15006 cfg = this.getAutoCreateTouchView();
15013 if(!this.tickable){
15014 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15019 * ComboBox with tickable selections
15022 var align = this.labelAlign || this.parentLabelAlign();
15025 cls : 'form-group roo-combobox-tickable' //input-group
15028 var btn_text_select = '';
15029 var btn_text_done = '';
15030 var btn_text_cancel = '';
15032 if (this.btn_text_show) {
15033 btn_text_select = 'Select';
15034 btn_text_done = 'Done';
15035 btn_text_cancel = 'Cancel';
15040 cls : 'tickable-buttons',
15045 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15046 //html : this.triggerText
15047 html: btn_text_select
15053 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15055 html: btn_text_done
15061 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15063 html: btn_text_cancel
15069 buttons.cn.unshift({
15071 cls: 'roo-select2-search-field-input'
15077 Roo.each(buttons.cn, function(c){
15079 c.cls += ' btn-' + _this.size;
15082 if (_this.disabled) {
15089 style : 'display: contents',
15094 cls: 'form-hidden-field'
15098 cls: 'roo-select2-choices',
15102 cls: 'roo-select2-search-field',
15113 cls: 'roo-select2-container input-group roo-select2-container-multi',
15119 // cls: 'typeahead typeahead-long dropdown-menu',
15120 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15125 if(this.hasFeedback && !this.allowBlank){
15129 cls: 'glyphicon form-control-feedback'
15132 combobox.cn.push(feedback);
15139 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15140 tooltip : 'This field is required'
15142 if (Roo.bootstrap.version == 4) {
15145 style : 'display:none'
15148 if (align ==='left' && this.fieldLabel.length) {
15150 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15157 cls : 'control-label col-form-label',
15158 html : this.fieldLabel
15170 var labelCfg = cfg.cn[1];
15171 var contentCfg = cfg.cn[2];
15174 if(this.indicatorpos == 'right'){
15180 cls : 'control-label col-form-label',
15184 html : this.fieldLabel
15200 labelCfg = cfg.cn[0];
15201 contentCfg = cfg.cn[1];
15205 if(this.labelWidth > 12){
15206 labelCfg.style = "width: " + this.labelWidth + 'px';
15209 if(this.labelWidth < 13 && this.labelmd == 0){
15210 this.labelmd = this.labelWidth;
15213 if(this.labellg > 0){
15214 labelCfg.cls += ' col-lg-' + this.labellg;
15215 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15218 if(this.labelmd > 0){
15219 labelCfg.cls += ' col-md-' + this.labelmd;
15220 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15223 if(this.labelsm > 0){
15224 labelCfg.cls += ' col-sm-' + this.labelsm;
15225 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15228 if(this.labelxs > 0){
15229 labelCfg.cls += ' col-xs-' + this.labelxs;
15230 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15234 } else if ( this.fieldLabel.length) {
15235 // Roo.log(" label");
15240 //cls : 'input-group-addon',
15241 html : this.fieldLabel
15246 if(this.indicatorpos == 'right'){
15250 //cls : 'input-group-addon',
15251 html : this.fieldLabel
15261 // Roo.log(" no label && no align");
15268 ['xs','sm','md','lg'].map(function(size){
15269 if (settings[size]) {
15270 cfg.cls += ' col-' + size + '-' + settings[size];
15278 _initEventsCalled : false,
15281 initEvents: function()
15283 if (this._initEventsCalled) { // as we call render... prevent looping...
15286 this._initEventsCalled = true;
15289 throw "can not find store for combo";
15292 this.indicator = this.indicatorEl();
15294 this.store = Roo.factory(this.store, Roo.data);
15295 this.store.parent = this;
15297 // if we are building from html. then this element is so complex, that we can not really
15298 // use the rendered HTML.
15299 // so we have to trash and replace the previous code.
15300 if (Roo.XComponent.build_from_html) {
15301 // remove this element....
15302 var e = this.el.dom, k=0;
15303 while (e ) { e = e.previousSibling; ++k;}
15308 this.rendered = false;
15310 this.render(this.parent().getChildContainer(true), k);
15313 if(Roo.isIOS && this.useNativeIOS){
15314 this.initIOSView();
15322 if(Roo.isTouch && this.mobileTouchView){
15323 this.initTouchView();
15328 this.initTickableEvents();
15332 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15334 if(this.hiddenName){
15336 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15338 this.hiddenField.dom.value =
15339 this.hiddenValue !== undefined ? this.hiddenValue :
15340 this.value !== undefined ? this.value : '';
15342 // prevent input submission
15343 this.el.dom.removeAttribute('name');
15344 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15349 // this.el.dom.setAttribute('autocomplete', 'off');
15352 var cls = 'x-combo-list';
15354 //this.list = new Roo.Layer({
15355 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15361 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15362 _this.list.setWidth(lw);
15365 this.list.on('mouseover', this.onViewOver, this);
15366 this.list.on('mousemove', this.onViewMove, this);
15367 this.list.on('scroll', this.onViewScroll, this);
15370 this.list.swallowEvent('mousewheel');
15371 this.assetHeight = 0;
15374 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15375 this.assetHeight += this.header.getHeight();
15378 this.innerList = this.list.createChild({cls:cls+'-inner'});
15379 this.innerList.on('mouseover', this.onViewOver, this);
15380 this.innerList.on('mousemove', this.onViewMove, this);
15381 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15383 if(this.allowBlank && !this.pageSize && !this.disableClear){
15384 this.footer = this.list.createChild({cls:cls+'-ft'});
15385 this.pageTb = new Roo.Toolbar(this.footer);
15389 this.footer = this.list.createChild({cls:cls+'-ft'});
15390 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15391 {pageSize: this.pageSize});
15395 if (this.pageTb && this.allowBlank && !this.disableClear) {
15397 this.pageTb.add(new Roo.Toolbar.Fill(), {
15398 cls: 'x-btn-icon x-btn-clear',
15400 handler: function()
15403 _this.clearValue();
15404 _this.onSelect(false, -1);
15409 this.assetHeight += this.footer.getHeight();
15414 this.tpl = Roo.bootstrap.version == 4 ?
15415 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15416 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15419 this.view = new Roo.View(this.list, this.tpl, {
15420 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15422 //this.view.wrapEl.setDisplayed(false);
15423 this.view.on('click', this.onViewClick, this);
15426 this.store.on('beforeload', this.onBeforeLoad, this);
15427 this.store.on('load', this.onLoad, this);
15428 this.store.on('loadexception', this.onLoadException, this);
15430 if(this.resizable){
15431 this.resizer = new Roo.Resizable(this.list, {
15432 pinned:true, handles:'se'
15434 this.resizer.on('resize', function(r, w, h){
15435 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15436 this.listWidth = w;
15437 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15438 this.restrictHeight();
15440 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15443 if(!this.editable){
15444 this.editable = true;
15445 this.setEditable(false);
15450 if (typeof(this.events.add.listeners) != 'undefined') {
15452 this.addicon = this.wrap.createChild(
15453 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15455 this.addicon.on('click', function(e) {
15456 this.fireEvent('add', this);
15459 if (typeof(this.events.edit.listeners) != 'undefined') {
15461 this.editicon = this.wrap.createChild(
15462 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15463 if (this.addicon) {
15464 this.editicon.setStyle('margin-left', '40px');
15466 this.editicon.on('click', function(e) {
15468 // we fire even if inothing is selected..
15469 this.fireEvent('edit', this, this.lastData );
15475 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15476 "up" : function(e){
15477 this.inKeyMode = true;
15481 "down" : function(e){
15482 if(!this.isExpanded()){
15483 this.onTriggerClick();
15485 this.inKeyMode = true;
15490 "enter" : function(e){
15491 // this.onViewClick();
15495 if(this.fireEvent("specialkey", this, e)){
15496 this.onViewClick(false);
15502 "esc" : function(e){
15506 "tab" : function(e){
15509 if(this.fireEvent("specialkey", this, e)){
15510 this.onViewClick(false);
15518 doRelay : function(foo, bar, hname){
15519 if(hname == 'down' || this.scope.isExpanded()){
15520 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15529 this.queryDelay = Math.max(this.queryDelay || 10,
15530 this.mode == 'local' ? 10 : 250);
15533 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15535 if(this.typeAhead){
15536 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15538 if(this.editable !== false){
15539 this.inputEl().on("keyup", this.onKeyUp, this);
15541 if(this.forceSelection){
15542 this.inputEl().on('blur', this.doForce, this);
15546 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15547 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15551 initTickableEvents: function()
15555 if(this.hiddenName){
15557 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15559 this.hiddenField.dom.value =
15560 this.hiddenValue !== undefined ? this.hiddenValue :
15561 this.value !== undefined ? this.value : '';
15563 // prevent input submission
15564 this.el.dom.removeAttribute('name');
15565 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15570 // this.list = this.el.select('ul.dropdown-menu',true).first();
15572 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15573 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15574 if(this.triggerList){
15575 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15578 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15579 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15581 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15582 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15584 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15585 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15587 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15588 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15589 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15592 this.cancelBtn.hide();
15597 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15598 _this.list.setWidth(lw);
15601 this.list.on('mouseover', this.onViewOver, this);
15602 this.list.on('mousemove', this.onViewMove, this);
15604 this.list.on('scroll', this.onViewScroll, this);
15607 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15608 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15611 this.view = new Roo.View(this.list, this.tpl, {
15616 selectedClass: this.selectedClass
15619 //this.view.wrapEl.setDisplayed(false);
15620 this.view.on('click', this.onViewClick, this);
15624 this.store.on('beforeload', this.onBeforeLoad, this);
15625 this.store.on('load', this.onLoad, this);
15626 this.store.on('loadexception', this.onLoadException, this);
15629 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15630 "up" : function(e){
15631 this.inKeyMode = true;
15635 "down" : function(e){
15636 this.inKeyMode = true;
15640 "enter" : function(e){
15641 if(this.fireEvent("specialkey", this, e)){
15642 this.onViewClick(false);
15648 "esc" : function(e){
15649 this.onTickableFooterButtonClick(e, false, false);
15652 "tab" : function(e){
15653 this.fireEvent("specialkey", this, e);
15655 this.onTickableFooterButtonClick(e, false, false);
15662 doRelay : function(e, fn, key){
15663 if(this.scope.isExpanded()){
15664 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15673 this.queryDelay = Math.max(this.queryDelay || 10,
15674 this.mode == 'local' ? 10 : 250);
15677 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15679 if(this.typeAhead){
15680 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15683 if(this.editable !== false){
15684 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15687 this.indicator = this.indicatorEl();
15689 if(this.indicator){
15690 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15691 this.indicator.hide();
15696 onDestroy : function(){
15698 this.view.setStore(null);
15699 this.view.el.removeAllListeners();
15700 this.view.el.remove();
15701 this.view.purgeListeners();
15704 this.list.dom.innerHTML = '';
15708 this.store.un('beforeload', this.onBeforeLoad, this);
15709 this.store.un('load', this.onLoad, this);
15710 this.store.un('loadexception', this.onLoadException, this);
15712 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15716 fireKey : function(e){
15717 if(e.isNavKeyPress() && !this.list.isVisible()){
15718 this.fireEvent("specialkey", this, e);
15723 onResize: function(w, h){
15724 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15726 // if(typeof w != 'number'){
15727 // // we do not handle it!?!?
15730 // var tw = this.trigger.getWidth();
15731 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15732 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15734 // this.inputEl().setWidth( this.adjustWidth('input', x));
15736 // //this.trigger.setStyle('left', x+'px');
15738 // if(this.list && this.listWidth === undefined){
15739 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15740 // this.list.setWidth(lw);
15741 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15749 * Allow or prevent the user from directly editing the field text. If false is passed,
15750 * the user will only be able to select from the items defined in the dropdown list. This method
15751 * is the runtime equivalent of setting the 'editable' config option at config time.
15752 * @param {Boolean} value True to allow the user to directly edit the field text
15754 setEditable : function(value){
15755 if(value == this.editable){
15758 this.editable = value;
15760 this.inputEl().dom.setAttribute('readOnly', true);
15761 this.inputEl().on('mousedown', this.onTriggerClick, this);
15762 this.inputEl().addClass('x-combo-noedit');
15764 this.inputEl().dom.setAttribute('readOnly', false);
15765 this.inputEl().un('mousedown', this.onTriggerClick, this);
15766 this.inputEl().removeClass('x-combo-noedit');
15772 onBeforeLoad : function(combo,opts){
15773 if(!this.hasFocus){
15777 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15779 this.restrictHeight();
15780 this.selectedIndex = -1;
15784 onLoad : function(){
15786 this.hasQuery = false;
15788 if(!this.hasFocus){
15792 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15793 this.loading.hide();
15796 if(this.store.getCount() > 0){
15799 this.restrictHeight();
15800 if(this.lastQuery == this.allQuery){
15801 if(this.editable && !this.tickable){
15802 this.inputEl().dom.select();
15806 !this.selectByValue(this.value, true) &&
15809 !this.store.lastOptions ||
15810 typeof(this.store.lastOptions.add) == 'undefined' ||
15811 this.store.lastOptions.add != true
15814 this.select(0, true);
15817 if(this.autoFocus){
15820 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15821 this.taTask.delay(this.typeAheadDelay);
15825 this.onEmptyResults();
15831 onLoadException : function()
15833 this.hasQuery = false;
15835 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15836 this.loading.hide();
15839 if(this.tickable && this.editable){
15844 // only causes errors at present
15845 //Roo.log(this.store.reader.jsonData);
15846 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15848 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15854 onTypeAhead : function(){
15855 if(this.store.getCount() > 0){
15856 var r = this.store.getAt(0);
15857 var newValue = r.data[this.displayField];
15858 var len = newValue.length;
15859 var selStart = this.getRawValue().length;
15861 if(selStart != len){
15862 this.setRawValue(newValue);
15863 this.selectText(selStart, newValue.length);
15869 onSelect : function(record, index){
15871 if(this.fireEvent('beforeselect', this, record, index) !== false){
15873 this.setFromData(index > -1 ? record.data : false);
15876 this.fireEvent('select', this, record, index);
15881 * Returns the currently selected field value or empty string if no value is set.
15882 * @return {String} value The selected value
15884 getValue : function()
15886 if(Roo.isIOS && this.useNativeIOS){
15887 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15891 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15894 if(this.valueField){
15895 return typeof this.value != 'undefined' ? this.value : '';
15897 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15901 getRawValue : function()
15903 if(Roo.isIOS && this.useNativeIOS){
15904 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15907 var v = this.inputEl().getValue();
15913 * Clears any text/value currently set in the field
15915 clearValue : function(){
15917 if(this.hiddenField){
15918 this.hiddenField.dom.value = '';
15921 this.setRawValue('');
15922 this.lastSelectionText = '';
15923 this.lastData = false;
15925 var close = this.closeTriggerEl();
15936 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15937 * will be displayed in the field. If the value does not match the data value of an existing item,
15938 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15939 * Otherwise the field will be blank (although the value will still be set).
15940 * @param {String} value The value to match
15942 setValue : function(v)
15944 if(Roo.isIOS && this.useNativeIOS){
15945 this.setIOSValue(v);
15955 if(this.valueField){
15956 var r = this.findRecord(this.valueField, v);
15958 text = r.data[this.displayField];
15959 }else if(this.valueNotFoundText !== undefined){
15960 text = this.valueNotFoundText;
15963 this.lastSelectionText = text;
15964 if(this.hiddenField){
15965 this.hiddenField.dom.value = v;
15967 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15970 var close = this.closeTriggerEl();
15973 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15979 * @property {Object} the last set data for the element
15984 * Sets the value of the field based on a object which is related to the record format for the store.
15985 * @param {Object} value the value to set as. or false on reset?
15987 setFromData : function(o){
15994 var dv = ''; // display value
15995 var vv = ''; // value value..
15997 if (this.displayField) {
15998 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16000 // this is an error condition!!!
16001 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16004 if(this.valueField){
16005 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16008 var close = this.closeTriggerEl();
16011 if(dv.length || vv * 1 > 0){
16013 this.blockFocus=true;
16019 if(this.hiddenField){
16020 this.hiddenField.dom.value = vv;
16022 this.lastSelectionText = dv;
16023 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16027 // no hidden field.. - we store the value in 'value', but still display
16028 // display field!!!!
16029 this.lastSelectionText = dv;
16030 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16037 reset : function(){
16038 // overridden so that last data is reset..
16045 this.setValue(this.originalValue);
16046 //this.clearInvalid();
16047 this.lastData = false;
16049 this.view.clearSelections();
16055 findRecord : function(prop, value){
16057 if(this.store.getCount() > 0){
16058 this.store.each(function(r){
16059 if(r.data[prop] == value){
16069 getName: function()
16071 // returns hidden if it's set..
16072 if (!this.rendered) {return ''};
16073 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16077 onViewMove : function(e, t){
16078 this.inKeyMode = false;
16082 onViewOver : function(e, t){
16083 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16086 var item = this.view.findItemFromChild(t);
16089 var index = this.view.indexOf(item);
16090 this.select(index, false);
16095 onViewClick : function(view, doFocus, el, e)
16097 var index = this.view.getSelectedIndexes()[0];
16099 var r = this.store.getAt(index);
16103 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16110 Roo.each(this.tickItems, function(v,k){
16112 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16114 _this.tickItems.splice(k, 1);
16116 if(typeof(e) == 'undefined' && view == false){
16117 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16129 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16130 this.tickItems.push(r.data);
16133 if(typeof(e) == 'undefined' && view == false){
16134 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16141 this.onSelect(r, index);
16143 if(doFocus !== false && !this.blockFocus){
16144 this.inputEl().focus();
16149 restrictHeight : function(){
16150 //this.innerList.dom.style.height = '';
16151 //var inner = this.innerList.dom;
16152 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16153 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16154 //this.list.beginUpdate();
16155 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16156 this.list.alignTo(this.inputEl(), this.listAlign);
16157 this.list.alignTo(this.inputEl(), this.listAlign);
16158 //this.list.endUpdate();
16162 onEmptyResults : function(){
16164 if(this.tickable && this.editable){
16165 this.hasFocus = false;
16166 this.restrictHeight();
16174 * Returns true if the dropdown list is expanded, else false.
16176 isExpanded : function(){
16177 return this.list.isVisible();
16181 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16182 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16183 * @param {String} value The data value of the item to select
16184 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16185 * selected item if it is not currently in view (defaults to true)
16186 * @return {Boolean} True if the value matched an item in the list, else false
16188 selectByValue : function(v, scrollIntoView){
16189 if(v !== undefined && v !== null){
16190 var r = this.findRecord(this.valueField || this.displayField, v);
16192 this.select(this.store.indexOf(r), scrollIntoView);
16200 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16201 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16202 * @param {Number} index The zero-based index of the list item to select
16203 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16204 * selected item if it is not currently in view (defaults to true)
16206 select : function(index, scrollIntoView){
16207 this.selectedIndex = index;
16208 this.view.select(index);
16209 if(scrollIntoView !== false){
16210 var el = this.view.getNode(index);
16212 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16215 this.list.scrollChildIntoView(el, false);
16221 selectNext : function(){
16222 var ct = this.store.getCount();
16224 if(this.selectedIndex == -1){
16226 }else if(this.selectedIndex < ct-1){
16227 this.select(this.selectedIndex+1);
16233 selectPrev : function(){
16234 var ct = this.store.getCount();
16236 if(this.selectedIndex == -1){
16238 }else if(this.selectedIndex != 0){
16239 this.select(this.selectedIndex-1);
16245 onKeyUp : function(e){
16246 if(this.editable !== false && !e.isSpecialKey()){
16247 this.lastKey = e.getKey();
16248 this.dqTask.delay(this.queryDelay);
16253 validateBlur : function(){
16254 return !this.list || !this.list.isVisible();
16258 initQuery : function(){
16260 var v = this.getRawValue();
16262 if(this.tickable && this.editable){
16263 v = this.tickableInputEl().getValue();
16270 doForce : function(){
16271 if(this.inputEl().dom.value.length > 0){
16272 this.inputEl().dom.value =
16273 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16279 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16280 * query allowing the query action to be canceled if needed.
16281 * @param {String} query The SQL query to execute
16282 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16283 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16284 * saved in the current store (defaults to false)
16286 doQuery : function(q, forceAll){
16288 if(q === undefined || q === null){
16293 forceAll: forceAll,
16297 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16302 forceAll = qe.forceAll;
16303 if(forceAll === true || (q.length >= this.minChars)){
16305 this.hasQuery = true;
16307 if(this.lastQuery != q || this.alwaysQuery){
16308 this.lastQuery = q;
16309 if(this.mode == 'local'){
16310 this.selectedIndex = -1;
16312 this.store.clearFilter();
16315 if(this.specialFilter){
16316 this.fireEvent('specialfilter', this);
16321 this.store.filter(this.displayField, q);
16324 this.store.fireEvent("datachanged", this.store);
16331 this.store.baseParams[this.queryParam] = q;
16333 var options = {params : this.getParams(q)};
16336 options.add = true;
16337 options.params.start = this.page * this.pageSize;
16340 this.store.load(options);
16343 * this code will make the page width larger, at the beginning, the list not align correctly,
16344 * we should expand the list on onLoad
16345 * so command out it
16350 this.selectedIndex = -1;
16355 this.loadNext = false;
16359 getParams : function(q){
16361 //p[this.queryParam] = q;
16365 p.limit = this.pageSize;
16371 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16373 collapse : function(){
16374 if(!this.isExpanded()){
16380 this.hasFocus = false;
16384 this.cancelBtn.hide();
16385 this.trigger.show();
16388 this.tickableInputEl().dom.value = '';
16389 this.tickableInputEl().blur();
16394 Roo.get(document).un('mousedown', this.collapseIf, this);
16395 Roo.get(document).un('mousewheel', this.collapseIf, this);
16396 if (!this.editable) {
16397 Roo.get(document).un('keydown', this.listKeyPress, this);
16399 this.fireEvent('collapse', this);
16405 collapseIf : function(e){
16406 var in_combo = e.within(this.el);
16407 var in_list = e.within(this.list);
16408 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16410 if (in_combo || in_list || is_list) {
16411 //e.stopPropagation();
16416 this.onTickableFooterButtonClick(e, false, false);
16424 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16426 expand : function(){
16428 if(this.isExpanded() || !this.hasFocus){
16432 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16433 this.list.setWidth(lw);
16439 this.restrictHeight();
16443 this.tickItems = Roo.apply([], this.item);
16446 this.cancelBtn.show();
16447 this.trigger.hide();
16450 this.tickableInputEl().focus();
16455 Roo.get(document).on('mousedown', this.collapseIf, this);
16456 Roo.get(document).on('mousewheel', this.collapseIf, this);
16457 if (!this.editable) {
16458 Roo.get(document).on('keydown', this.listKeyPress, this);
16461 this.fireEvent('expand', this);
16465 // Implements the default empty TriggerField.onTriggerClick function
16466 onTriggerClick : function(e)
16468 Roo.log('trigger click');
16470 if(this.disabled || !this.triggerList){
16475 this.loadNext = false;
16477 if(this.isExpanded()){
16479 if (!this.blockFocus) {
16480 this.inputEl().focus();
16484 this.hasFocus = true;
16485 if(this.triggerAction == 'all') {
16486 this.doQuery(this.allQuery, true);
16488 this.doQuery(this.getRawValue());
16490 if (!this.blockFocus) {
16491 this.inputEl().focus();
16496 onTickableTriggerClick : function(e)
16503 this.loadNext = false;
16504 this.hasFocus = true;
16506 if(this.triggerAction == 'all') {
16507 this.doQuery(this.allQuery, true);
16509 this.doQuery(this.getRawValue());
16513 onSearchFieldClick : function(e)
16515 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16516 this.onTickableFooterButtonClick(e, false, false);
16520 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16525 this.loadNext = false;
16526 this.hasFocus = true;
16528 if(this.triggerAction == 'all') {
16529 this.doQuery(this.allQuery, true);
16531 this.doQuery(this.getRawValue());
16535 listKeyPress : function(e)
16537 //Roo.log('listkeypress');
16538 // scroll to first matching element based on key pres..
16539 if (e.isSpecialKey()) {
16542 var k = String.fromCharCode(e.getKey()).toUpperCase();
16545 var csel = this.view.getSelectedNodes();
16546 var cselitem = false;
16548 var ix = this.view.indexOf(csel[0]);
16549 cselitem = this.store.getAt(ix);
16550 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16556 this.store.each(function(v) {
16558 // start at existing selection.
16559 if (cselitem.id == v.id) {
16565 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16566 match = this.store.indexOf(v);
16572 if (match === false) {
16573 return true; // no more action?
16576 this.view.select(match);
16577 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16578 sn.scrollIntoView(sn.dom.parentNode, false);
16581 onViewScroll : function(e, t){
16583 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){
16587 this.hasQuery = true;
16589 this.loading = this.list.select('.loading', true).first();
16591 if(this.loading === null){
16592 this.list.createChild({
16594 cls: 'loading roo-select2-more-results roo-select2-active',
16595 html: 'Loading more results...'
16598 this.loading = this.list.select('.loading', true).first();
16600 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16602 this.loading.hide();
16605 this.loading.show();
16610 this.loadNext = true;
16612 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16617 addItem : function(o)
16619 var dv = ''; // display value
16621 if (this.displayField) {
16622 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16624 // this is an error condition!!!
16625 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16632 var choice = this.choices.createChild({
16634 cls: 'roo-select2-search-choice',
16643 cls: 'roo-select2-search-choice-close fa fa-times',
16648 }, this.searchField);
16650 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16652 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16660 this.inputEl().dom.value = '';
16665 onRemoveItem : function(e, _self, o)
16667 e.preventDefault();
16669 this.lastItem = Roo.apply([], this.item);
16671 var index = this.item.indexOf(o.data) * 1;
16674 Roo.log('not this item?!');
16678 this.item.splice(index, 1);
16683 this.fireEvent('remove', this, e);
16689 syncValue : function()
16691 if(!this.item.length){
16698 Roo.each(this.item, function(i){
16699 if(_this.valueField){
16700 value.push(i[_this.valueField]);
16707 this.value = value.join(',');
16709 if(this.hiddenField){
16710 this.hiddenField.dom.value = this.value;
16713 this.store.fireEvent("datachanged", this.store);
16718 clearItem : function()
16720 if(!this.multiple){
16726 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16734 if(this.tickable && !Roo.isTouch){
16735 this.view.refresh();
16739 inputEl: function ()
16741 if(Roo.isIOS && this.useNativeIOS){
16742 return this.el.select('select.roo-ios-select', true).first();
16745 if(Roo.isTouch && this.mobileTouchView){
16746 return this.el.select('input.form-control',true).first();
16750 return this.searchField;
16753 return this.el.select('input.form-control',true).first();
16756 onTickableFooterButtonClick : function(e, btn, el)
16758 e.preventDefault();
16760 this.lastItem = Roo.apply([], this.item);
16762 if(btn && btn.name == 'cancel'){
16763 this.tickItems = Roo.apply([], this.item);
16772 Roo.each(this.tickItems, function(o){
16780 validate : function()
16782 if(this.getVisibilityEl().hasClass('hidden')){
16786 var v = this.getRawValue();
16789 v = this.getValue();
16792 if(this.disabled || this.allowBlank || v.length){
16797 this.markInvalid();
16801 tickableInputEl : function()
16803 if(!this.tickable || !this.editable){
16804 return this.inputEl();
16807 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16811 getAutoCreateTouchView : function()
16816 cls: 'form-group' //input-group
16822 type : this.inputType,
16823 cls : 'form-control x-combo-noedit',
16824 autocomplete: 'new-password',
16825 placeholder : this.placeholder || '',
16830 input.name = this.name;
16834 input.cls += ' input-' + this.size;
16837 if (this.disabled) {
16838 input.disabled = true;
16849 inputblock.cls += ' input-group';
16851 inputblock.cn.unshift({
16853 cls : 'input-group-addon input-group-prepend input-group-text',
16858 if(this.removable && !this.multiple){
16859 inputblock.cls += ' roo-removable';
16861 inputblock.cn.push({
16864 cls : 'roo-combo-removable-btn close'
16868 if(this.hasFeedback && !this.allowBlank){
16870 inputblock.cls += ' has-feedback';
16872 inputblock.cn.push({
16874 cls: 'glyphicon form-control-feedback'
16881 inputblock.cls += (this.before) ? '' : ' input-group';
16883 inputblock.cn.push({
16885 cls : 'input-group-addon input-group-append input-group-text',
16891 var ibwrap = inputblock;
16896 cls: 'roo-select2-choices',
16900 cls: 'roo-select2-search-field',
16913 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16918 cls: 'form-hidden-field'
16924 if(!this.multiple && this.showToggleBtn){
16930 if (this.caret != false) {
16933 cls: 'fa fa-' + this.caret
16940 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16942 Roo.bootstrap.version == 3 ? caret : '',
16945 cls: 'combobox-clear',
16959 combobox.cls += ' roo-select2-container-multi';
16962 var align = this.labelAlign || this.parentLabelAlign();
16964 if (align ==='left' && this.fieldLabel.length) {
16969 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16970 tooltip : 'This field is required'
16974 cls : 'control-label col-form-label',
16975 html : this.fieldLabel
16986 var labelCfg = cfg.cn[1];
16987 var contentCfg = cfg.cn[2];
16990 if(this.indicatorpos == 'right'){
16995 cls : 'control-label col-form-label',
16999 html : this.fieldLabel
17003 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17004 tooltip : 'This field is required'
17017 labelCfg = cfg.cn[0];
17018 contentCfg = cfg.cn[1];
17023 if(this.labelWidth > 12){
17024 labelCfg.style = "width: " + this.labelWidth + 'px';
17027 if(this.labelWidth < 13 && this.labelmd == 0){
17028 this.labelmd = this.labelWidth;
17031 if(this.labellg > 0){
17032 labelCfg.cls += ' col-lg-' + this.labellg;
17033 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17036 if(this.labelmd > 0){
17037 labelCfg.cls += ' col-md-' + this.labelmd;
17038 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17041 if(this.labelsm > 0){
17042 labelCfg.cls += ' col-sm-' + this.labelsm;
17043 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17046 if(this.labelxs > 0){
17047 labelCfg.cls += ' col-xs-' + this.labelxs;
17048 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17052 } else if ( this.fieldLabel.length) {
17056 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17057 tooltip : 'This field is required'
17061 cls : 'control-label',
17062 html : this.fieldLabel
17073 if(this.indicatorpos == 'right'){
17077 cls : 'control-label',
17078 html : this.fieldLabel,
17082 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17083 tooltip : 'This field is required'
17100 var settings = this;
17102 ['xs','sm','md','lg'].map(function(size){
17103 if (settings[size]) {
17104 cfg.cls += ' col-' + size + '-' + settings[size];
17111 initTouchView : function()
17113 this.renderTouchView();
17115 this.touchViewEl.on('scroll', function(){
17116 this.el.dom.scrollTop = 0;
17119 this.originalValue = this.getValue();
17121 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17123 this.inputEl().on("click", this.showTouchView, this);
17124 if (this.triggerEl) {
17125 this.triggerEl.on("click", this.showTouchView, this);
17129 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17130 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17132 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17134 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17135 this.store.on('load', this.onTouchViewLoad, this);
17136 this.store.on('loadexception', this.onTouchViewLoadException, this);
17138 if(this.hiddenName){
17140 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17142 this.hiddenField.dom.value =
17143 this.hiddenValue !== undefined ? this.hiddenValue :
17144 this.value !== undefined ? this.value : '';
17146 this.el.dom.removeAttribute('name');
17147 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17151 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17152 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17155 if(this.removable && !this.multiple){
17156 var close = this.closeTriggerEl();
17158 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17159 close.on('click', this.removeBtnClick, this, close);
17163 * fix the bug in Safari iOS8
17165 this.inputEl().on("focus", function(e){
17166 document.activeElement.blur();
17169 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17176 renderTouchView : function()
17178 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17179 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17181 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17182 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17184 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17185 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17186 this.touchViewBodyEl.setStyle('overflow', 'auto');
17188 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17189 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17191 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17192 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17196 showTouchView : function()
17202 this.touchViewHeaderEl.hide();
17204 if(this.modalTitle.length){
17205 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17206 this.touchViewHeaderEl.show();
17209 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17210 this.touchViewEl.show();
17212 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17214 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17215 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17217 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17219 if(this.modalTitle.length){
17220 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17223 this.touchViewBodyEl.setHeight(bodyHeight);
17227 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17229 this.touchViewEl.addClass('in');
17232 if(this._touchViewMask){
17233 Roo.get(document.body).addClass("x-body-masked");
17234 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17235 this._touchViewMask.setStyle('z-index', 10000);
17236 this._touchViewMask.addClass('show');
17239 this.doTouchViewQuery();
17243 hideTouchView : function()
17245 this.touchViewEl.removeClass('in');
17249 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17251 this.touchViewEl.setStyle('display', 'none');
17254 if(this._touchViewMask){
17255 this._touchViewMask.removeClass('show');
17256 Roo.get(document.body).removeClass("x-body-masked");
17260 setTouchViewValue : function()
17267 Roo.each(this.tickItems, function(o){
17272 this.hideTouchView();
17275 doTouchViewQuery : function()
17284 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17288 if(!this.alwaysQuery || this.mode == 'local'){
17289 this.onTouchViewLoad();
17296 onTouchViewBeforeLoad : function(combo,opts)
17302 onTouchViewLoad : function()
17304 if(this.store.getCount() < 1){
17305 this.onTouchViewEmptyResults();
17309 this.clearTouchView();
17311 var rawValue = this.getRawValue();
17313 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17315 this.tickItems = [];
17317 this.store.data.each(function(d, rowIndex){
17318 var row = this.touchViewListGroup.createChild(template);
17320 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17321 row.addClass(d.data.cls);
17324 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17327 html : d.data[this.displayField]
17330 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17331 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17334 row.removeClass('selected');
17335 if(!this.multiple && this.valueField &&
17336 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17339 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17340 row.addClass('selected');
17343 if(this.multiple && this.valueField &&
17344 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17348 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17349 this.tickItems.push(d.data);
17352 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17356 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17358 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17360 if(this.modalTitle.length){
17361 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17364 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17366 if(this.mobile_restrict_height && listHeight < bodyHeight){
17367 this.touchViewBodyEl.setHeight(listHeight);
17372 if(firstChecked && listHeight > bodyHeight){
17373 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17378 onTouchViewLoadException : function()
17380 this.hideTouchView();
17383 onTouchViewEmptyResults : function()
17385 this.clearTouchView();
17387 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17389 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17393 clearTouchView : function()
17395 this.touchViewListGroup.dom.innerHTML = '';
17398 onTouchViewClick : function(e, el, o)
17400 e.preventDefault();
17403 var rowIndex = o.rowIndex;
17405 var r = this.store.getAt(rowIndex);
17407 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17409 if(!this.multiple){
17410 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17411 c.dom.removeAttribute('checked');
17414 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17416 this.setFromData(r.data);
17418 var close = this.closeTriggerEl();
17424 this.hideTouchView();
17426 this.fireEvent('select', this, r, rowIndex);
17431 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17432 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17433 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17437 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17438 this.addItem(r.data);
17439 this.tickItems.push(r.data);
17443 getAutoCreateNativeIOS : function()
17446 cls: 'form-group' //input-group,
17451 cls : 'roo-ios-select'
17455 combobox.name = this.name;
17458 if (this.disabled) {
17459 combobox.disabled = true;
17462 var settings = this;
17464 ['xs','sm','md','lg'].map(function(size){
17465 if (settings[size]) {
17466 cfg.cls += ' col-' + size + '-' + settings[size];
17476 initIOSView : function()
17478 this.store.on('load', this.onIOSViewLoad, this);
17483 onIOSViewLoad : function()
17485 if(this.store.getCount() < 1){
17489 this.clearIOSView();
17491 if(this.allowBlank) {
17493 var default_text = '-- SELECT --';
17495 if(this.placeholder.length){
17496 default_text = this.placeholder;
17499 if(this.emptyTitle.length){
17500 default_text += ' - ' + this.emptyTitle + ' -';
17503 var opt = this.inputEl().createChild({
17506 html : default_text
17510 o[this.valueField] = 0;
17511 o[this.displayField] = default_text;
17513 this.ios_options.push({
17520 this.store.data.each(function(d, rowIndex){
17524 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17525 html = d.data[this.displayField];
17530 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17531 value = d.data[this.valueField];
17540 if(this.value == d.data[this.valueField]){
17541 option['selected'] = true;
17544 var opt = this.inputEl().createChild(option);
17546 this.ios_options.push({
17553 this.inputEl().on('change', function(){
17554 this.fireEvent('select', this);
17559 clearIOSView: function()
17561 this.inputEl().dom.innerHTML = '';
17563 this.ios_options = [];
17566 setIOSValue: function(v)
17570 if(!this.ios_options){
17574 Roo.each(this.ios_options, function(opts){
17576 opts.el.dom.removeAttribute('selected');
17578 if(opts.data[this.valueField] != v){
17582 opts.el.dom.setAttribute('selected', true);
17588 * @cfg {Boolean} grow
17592 * @cfg {Number} growMin
17596 * @cfg {Number} growMax
17605 Roo.apply(Roo.bootstrap.ComboBox, {
17609 cls: 'modal-header',
17631 cls: 'list-group-item',
17635 cls: 'roo-combobox-list-group-item-value'
17639 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17653 listItemCheckbox : {
17655 cls: 'list-group-item',
17659 cls: 'roo-combobox-list-group-item-value'
17663 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17679 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17684 cls: 'modal-footer',
17692 cls: 'col-xs-6 text-left',
17695 cls: 'btn btn-danger roo-touch-view-cancel',
17701 cls: 'col-xs-6 text-right',
17704 cls: 'btn btn-success roo-touch-view-ok',
17715 Roo.apply(Roo.bootstrap.ComboBox, {
17717 touchViewTemplate : {
17719 cls: 'modal fade roo-combobox-touch-view',
17723 cls: 'modal-dialog',
17724 style : 'position:fixed', // we have to fix position....
17728 cls: 'modal-content',
17730 Roo.bootstrap.ComboBox.header,
17731 Roo.bootstrap.ComboBox.body,
17732 Roo.bootstrap.ComboBox.footer
17741 * Ext JS Library 1.1.1
17742 * Copyright(c) 2006-2007, Ext JS, LLC.
17744 * Originally Released Under LGPL - original licence link has changed is not relivant.
17747 * <script type="text/javascript">
17752 * @extends Roo.util.Observable
17753 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17754 * This class also supports single and multi selection modes. <br>
17755 * Create a data model bound view:
17757 var store = new Roo.data.Store(...);
17759 var view = new Roo.View({
17761 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17763 singleSelect: true,
17764 selectedClass: "ydataview-selected",
17768 // listen for node click?
17769 view.on("click", function(vw, index, node, e){
17770 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17774 dataModel.load("foobar.xml");
17776 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17778 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17779 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17781 * Note: old style constructor is still suported (container, template, config)
17784 * Create a new View
17785 * @param {Object} config The config object
17788 Roo.View = function(config, depreciated_tpl, depreciated_config){
17790 this.parent = false;
17792 if (typeof(depreciated_tpl) == 'undefined') {
17793 // new way.. - universal constructor.
17794 Roo.apply(this, config);
17795 this.el = Roo.get(this.el);
17798 this.el = Roo.get(config);
17799 this.tpl = depreciated_tpl;
17800 Roo.apply(this, depreciated_config);
17802 this.wrapEl = this.el.wrap().wrap();
17803 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17806 if(typeof(this.tpl) == "string"){
17807 this.tpl = new Roo.Template(this.tpl);
17809 // support xtype ctors..
17810 this.tpl = new Roo.factory(this.tpl, Roo);
17814 this.tpl.compile();
17819 * @event beforeclick
17820 * Fires before a click is processed. Returns false to cancel the default action.
17821 * @param {Roo.View} this
17822 * @param {Number} index The index of the target node
17823 * @param {HTMLElement} node The target node
17824 * @param {Roo.EventObject} e The raw event object
17826 "beforeclick" : true,
17829 * Fires when a template node is clicked.
17830 * @param {Roo.View} this
17831 * @param {Number} index The index of the target node
17832 * @param {HTMLElement} node The target node
17833 * @param {Roo.EventObject} e The raw event object
17838 * Fires when a template node is double clicked.
17839 * @param {Roo.View} this
17840 * @param {Number} index The index of the target node
17841 * @param {HTMLElement} node The target node
17842 * @param {Roo.EventObject} e The raw event object
17846 * @event contextmenu
17847 * Fires when a template node is right clicked.
17848 * @param {Roo.View} this
17849 * @param {Number} index The index of the target node
17850 * @param {HTMLElement} node The target node
17851 * @param {Roo.EventObject} e The raw event object
17853 "contextmenu" : true,
17855 * @event selectionchange
17856 * Fires when the selected nodes change.
17857 * @param {Roo.View} this
17858 * @param {Array} selections Array of the selected nodes
17860 "selectionchange" : true,
17863 * @event beforeselect
17864 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17865 * @param {Roo.View} this
17866 * @param {HTMLElement} node The node to be selected
17867 * @param {Array} selections Array of currently selected nodes
17869 "beforeselect" : true,
17871 * @event preparedata
17872 * Fires on every row to render, to allow you to change the data.
17873 * @param {Roo.View} this
17874 * @param {Object} data to be rendered (change this)
17876 "preparedata" : true
17884 "click": this.onClick,
17885 "dblclick": this.onDblClick,
17886 "contextmenu": this.onContextMenu,
17890 this.selections = [];
17892 this.cmp = new Roo.CompositeElementLite([]);
17894 this.store = Roo.factory(this.store, Roo.data);
17895 this.setStore(this.store, true);
17898 if ( this.footer && this.footer.xtype) {
17900 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17902 this.footer.dataSource = this.store;
17903 this.footer.container = fctr;
17904 this.footer = Roo.factory(this.footer, Roo);
17905 fctr.insertFirst(this.el);
17907 // this is a bit insane - as the paging toolbar seems to detach the el..
17908 // dom.parentNode.parentNode.parentNode
17909 // they get detached?
17913 Roo.View.superclass.constructor.call(this);
17918 Roo.extend(Roo.View, Roo.util.Observable, {
17921 * @cfg {Roo.data.Store} store Data store to load data from.
17926 * @cfg {String|Roo.Element} el The container element.
17931 * @cfg {String|Roo.Template} tpl The template used by this View
17935 * @cfg {String} dataName the named area of the template to use as the data area
17936 * Works with domtemplates roo-name="name"
17940 * @cfg {String} selectedClass The css class to add to selected nodes
17942 selectedClass : "x-view-selected",
17944 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17949 * @cfg {String} text to display on mask (default Loading)
17953 * @cfg {Boolean} multiSelect Allow multiple selection
17955 multiSelect : false,
17957 * @cfg {Boolean} singleSelect Allow single selection
17959 singleSelect: false,
17962 * @cfg {Boolean} toggleSelect - selecting
17964 toggleSelect : false,
17967 * @cfg {Boolean} tickable - selecting
17972 * Returns the element this view is bound to.
17973 * @return {Roo.Element}
17975 getEl : function(){
17976 return this.wrapEl;
17982 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17984 refresh : function(){
17985 //Roo.log('refresh');
17988 // if we are using something like 'domtemplate', then
17989 // the what gets used is:
17990 // t.applySubtemplate(NAME, data, wrapping data..)
17991 // the outer template then get' applied with
17992 // the store 'extra data'
17993 // and the body get's added to the
17994 // roo-name="data" node?
17995 // <span class='roo-tpl-{name}'></span> ?????
17999 this.clearSelections();
18000 this.el.update("");
18002 var records = this.store.getRange();
18003 if(records.length < 1) {
18005 // is this valid?? = should it render a template??
18007 this.el.update(this.emptyText);
18011 if (this.dataName) {
18012 this.el.update(t.apply(this.store.meta)); //????
18013 el = this.el.child('.roo-tpl-' + this.dataName);
18016 for(var i = 0, len = records.length; i < len; i++){
18017 var data = this.prepareData(records[i].data, i, records[i]);
18018 this.fireEvent("preparedata", this, data, i, records[i]);
18020 var d = Roo.apply({}, data);
18023 Roo.apply(d, {'roo-id' : Roo.id()});
18027 Roo.each(this.parent.item, function(item){
18028 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18031 Roo.apply(d, {'roo-data-checked' : 'checked'});
18035 html[html.length] = Roo.util.Format.trim(
18037 t.applySubtemplate(this.dataName, d, this.store.meta) :
18044 el.update(html.join(""));
18045 this.nodes = el.dom.childNodes;
18046 this.updateIndexes(0);
18051 * Function to override to reformat the data that is sent to
18052 * the template for each node.
18053 * DEPRICATED - use the preparedata event handler.
18054 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18055 * a JSON object for an UpdateManager bound view).
18057 prepareData : function(data, index, record)
18059 this.fireEvent("preparedata", this, data, index, record);
18063 onUpdate : function(ds, record){
18064 // Roo.log('on update');
18065 this.clearSelections();
18066 var index = this.store.indexOf(record);
18067 var n = this.nodes[index];
18068 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18069 n.parentNode.removeChild(n);
18070 this.updateIndexes(index, index);
18076 onAdd : function(ds, records, index)
18078 //Roo.log(['on Add', ds, records, index] );
18079 this.clearSelections();
18080 if(this.nodes.length == 0){
18084 var n = this.nodes[index];
18085 for(var i = 0, len = records.length; i < len; i++){
18086 var d = this.prepareData(records[i].data, i, records[i]);
18088 this.tpl.insertBefore(n, d);
18091 this.tpl.append(this.el, d);
18094 this.updateIndexes(index);
18097 onRemove : function(ds, record, index){
18098 // Roo.log('onRemove');
18099 this.clearSelections();
18100 var el = this.dataName ?
18101 this.el.child('.roo-tpl-' + this.dataName) :
18104 el.dom.removeChild(this.nodes[index]);
18105 this.updateIndexes(index);
18109 * Refresh an individual node.
18110 * @param {Number} index
18112 refreshNode : function(index){
18113 this.onUpdate(this.store, this.store.getAt(index));
18116 updateIndexes : function(startIndex, endIndex){
18117 var ns = this.nodes;
18118 startIndex = startIndex || 0;
18119 endIndex = endIndex || ns.length - 1;
18120 for(var i = startIndex; i <= endIndex; i++){
18121 ns[i].nodeIndex = i;
18126 * Changes the data store this view uses and refresh the view.
18127 * @param {Store} store
18129 setStore : function(store, initial){
18130 if(!initial && this.store){
18131 this.store.un("datachanged", this.refresh);
18132 this.store.un("add", this.onAdd);
18133 this.store.un("remove", this.onRemove);
18134 this.store.un("update", this.onUpdate);
18135 this.store.un("clear", this.refresh);
18136 this.store.un("beforeload", this.onBeforeLoad);
18137 this.store.un("load", this.onLoad);
18138 this.store.un("loadexception", this.onLoad);
18142 store.on("datachanged", this.refresh, this);
18143 store.on("add", this.onAdd, this);
18144 store.on("remove", this.onRemove, this);
18145 store.on("update", this.onUpdate, this);
18146 store.on("clear", this.refresh, this);
18147 store.on("beforeload", this.onBeforeLoad, this);
18148 store.on("load", this.onLoad, this);
18149 store.on("loadexception", this.onLoad, this);
18157 * onbeforeLoad - masks the loading area.
18160 onBeforeLoad : function(store,opts)
18162 //Roo.log('onBeforeLoad');
18164 this.el.update("");
18166 this.el.mask(this.mask ? this.mask : "Loading" );
18168 onLoad : function ()
18175 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18176 * @param {HTMLElement} node
18177 * @return {HTMLElement} The template node
18179 findItemFromChild : function(node){
18180 var el = this.dataName ?
18181 this.el.child('.roo-tpl-' + this.dataName,true) :
18184 if(!node || node.parentNode == el){
18187 var p = node.parentNode;
18188 while(p && p != el){
18189 if(p.parentNode == el){
18198 onClick : function(e){
18199 var item = this.findItemFromChild(e.getTarget());
18201 var index = this.indexOf(item);
18202 if(this.onItemClick(item, index, e) !== false){
18203 this.fireEvent("click", this, index, item, e);
18206 this.clearSelections();
18211 onContextMenu : function(e){
18212 var item = this.findItemFromChild(e.getTarget());
18214 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18219 onDblClick : function(e){
18220 var item = this.findItemFromChild(e.getTarget());
18222 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18226 onItemClick : function(item, index, e)
18228 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18231 if (this.toggleSelect) {
18232 var m = this.isSelected(item) ? 'unselect' : 'select';
18235 _t[m](item, true, false);
18238 if(this.multiSelect || this.singleSelect){
18239 if(this.multiSelect && e.shiftKey && this.lastSelection){
18240 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18242 this.select(item, this.multiSelect && e.ctrlKey);
18243 this.lastSelection = item;
18246 if(!this.tickable){
18247 e.preventDefault();
18255 * Get the number of selected nodes.
18258 getSelectionCount : function(){
18259 return this.selections.length;
18263 * Get the currently selected nodes.
18264 * @return {Array} An array of HTMLElements
18266 getSelectedNodes : function(){
18267 return this.selections;
18271 * Get the indexes of the selected nodes.
18274 getSelectedIndexes : function(){
18275 var indexes = [], s = this.selections;
18276 for(var i = 0, len = s.length; i < len; i++){
18277 indexes.push(s[i].nodeIndex);
18283 * Clear all selections
18284 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18286 clearSelections : function(suppressEvent){
18287 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18288 this.cmp.elements = this.selections;
18289 this.cmp.removeClass(this.selectedClass);
18290 this.selections = [];
18291 if(!suppressEvent){
18292 this.fireEvent("selectionchange", this, this.selections);
18298 * Returns true if the passed node is selected
18299 * @param {HTMLElement/Number} node The node or node index
18300 * @return {Boolean}
18302 isSelected : function(node){
18303 var s = this.selections;
18307 node = this.getNode(node);
18308 return s.indexOf(node) !== -1;
18313 * @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
18314 * @param {Boolean} keepExisting (optional) true to keep existing selections
18315 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18317 select : function(nodeInfo, keepExisting, suppressEvent){
18318 if(nodeInfo instanceof Array){
18320 this.clearSelections(true);
18322 for(var i = 0, len = nodeInfo.length; i < len; i++){
18323 this.select(nodeInfo[i], true, true);
18327 var node = this.getNode(nodeInfo);
18328 if(!node || this.isSelected(node)){
18329 return; // already selected.
18332 this.clearSelections(true);
18335 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18336 Roo.fly(node).addClass(this.selectedClass);
18337 this.selections.push(node);
18338 if(!suppressEvent){
18339 this.fireEvent("selectionchange", this, this.selections);
18347 * @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
18348 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18349 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18351 unselect : function(nodeInfo, keepExisting, suppressEvent)
18353 if(nodeInfo instanceof Array){
18354 Roo.each(this.selections, function(s) {
18355 this.unselect(s, nodeInfo);
18359 var node = this.getNode(nodeInfo);
18360 if(!node || !this.isSelected(node)){
18361 //Roo.log("not selected");
18362 return; // not selected.
18366 Roo.each(this.selections, function(s) {
18368 Roo.fly(node).removeClass(this.selectedClass);
18375 this.selections= ns;
18376 this.fireEvent("selectionchange", this, this.selections);
18380 * Gets a template node.
18381 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18382 * @return {HTMLElement} The node or null if it wasn't found
18384 getNode : function(nodeInfo){
18385 if(typeof nodeInfo == "string"){
18386 return document.getElementById(nodeInfo);
18387 }else if(typeof nodeInfo == "number"){
18388 return this.nodes[nodeInfo];
18394 * Gets a range template nodes.
18395 * @param {Number} startIndex
18396 * @param {Number} endIndex
18397 * @return {Array} An array of nodes
18399 getNodes : function(start, end){
18400 var ns = this.nodes;
18401 start = start || 0;
18402 end = typeof end == "undefined" ? ns.length - 1 : end;
18405 for(var i = start; i <= end; i++){
18409 for(var i = start; i >= end; i--){
18417 * Finds the index of the passed node
18418 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18419 * @return {Number} The index of the node or -1
18421 indexOf : function(node){
18422 node = this.getNode(node);
18423 if(typeof node.nodeIndex == "number"){
18424 return node.nodeIndex;
18426 var ns = this.nodes;
18427 for(var i = 0, len = ns.length; i < len; i++){
18438 * based on jquery fullcalendar
18442 Roo.bootstrap = Roo.bootstrap || {};
18444 * @class Roo.bootstrap.Calendar
18445 * @extends Roo.bootstrap.Component
18446 * Bootstrap Calendar class
18447 * @cfg {Boolean} loadMask (true|false) default false
18448 * @cfg {Object} header generate the user specific header of the calendar, default false
18451 * Create a new Container
18452 * @param {Object} config The config object
18457 Roo.bootstrap.Calendar = function(config){
18458 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18462 * Fires when a date is selected
18463 * @param {DatePicker} this
18464 * @param {Date} date The selected date
18468 * @event monthchange
18469 * Fires when the displayed month changes
18470 * @param {DatePicker} this
18471 * @param {Date} date The selected month
18473 'monthchange': true,
18475 * @event evententer
18476 * Fires when mouse over an event
18477 * @param {Calendar} this
18478 * @param {event} Event
18480 'evententer': true,
18482 * @event eventleave
18483 * Fires when the mouse leaves an
18484 * @param {Calendar} this
18487 'eventleave': true,
18489 * @event eventclick
18490 * Fires when the mouse click an
18491 * @param {Calendar} this
18500 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18503 * @cfg {Number} startDay
18504 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18512 getAutoCreate : function(){
18515 var fc_button = function(name, corner, style, content ) {
18516 return Roo.apply({},{
18518 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18520 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18523 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18534 style : 'width:100%',
18541 cls : 'fc-header-left',
18543 fc_button('prev', 'left', 'arrow', '‹' ),
18544 fc_button('next', 'right', 'arrow', '›' ),
18545 { tag: 'span', cls: 'fc-header-space' },
18546 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18554 cls : 'fc-header-center',
18558 cls: 'fc-header-title',
18561 html : 'month / year'
18569 cls : 'fc-header-right',
18571 /* fc_button('month', 'left', '', 'month' ),
18572 fc_button('week', '', '', 'week' ),
18573 fc_button('day', 'right', '', 'day' )
18585 header = this.header;
18588 var cal_heads = function() {
18590 // fixme - handle this.
18592 for (var i =0; i < Date.dayNames.length; i++) {
18593 var d = Date.dayNames[i];
18596 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18597 html : d.substring(0,3)
18601 ret[0].cls += ' fc-first';
18602 ret[6].cls += ' fc-last';
18605 var cal_cell = function(n) {
18608 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18613 cls: 'fc-day-number',
18617 cls: 'fc-day-content',
18621 style: 'position: relative;' // height: 17px;
18633 var cal_rows = function() {
18636 for (var r = 0; r < 6; r++) {
18643 for (var i =0; i < Date.dayNames.length; i++) {
18644 var d = Date.dayNames[i];
18645 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18648 row.cn[0].cls+=' fc-first';
18649 row.cn[0].cn[0].style = 'min-height:90px';
18650 row.cn[6].cls+=' fc-last';
18654 ret[0].cls += ' fc-first';
18655 ret[4].cls += ' fc-prev-last';
18656 ret[5].cls += ' fc-last';
18663 cls: 'fc-border-separate',
18664 style : 'width:100%',
18672 cls : 'fc-first fc-last',
18690 cls : 'fc-content',
18691 style : "position: relative;",
18694 cls : 'fc-view fc-view-month fc-grid',
18695 style : 'position: relative',
18696 unselectable : 'on',
18699 cls : 'fc-event-container',
18700 style : 'position:absolute;z-index:8;top:0;left:0;'
18718 initEvents : function()
18721 throw "can not find store for calendar";
18727 style: "text-align:center",
18731 style: "background-color:white;width:50%;margin:250 auto",
18735 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18746 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18748 var size = this.el.select('.fc-content', true).first().getSize();
18749 this.maskEl.setSize(size.width, size.height);
18750 this.maskEl.enableDisplayMode("block");
18751 if(!this.loadMask){
18752 this.maskEl.hide();
18755 this.store = Roo.factory(this.store, Roo.data);
18756 this.store.on('load', this.onLoad, this);
18757 this.store.on('beforeload', this.onBeforeLoad, this);
18761 this.cells = this.el.select('.fc-day',true);
18762 //Roo.log(this.cells);
18763 this.textNodes = this.el.query('.fc-day-number');
18764 this.cells.addClassOnOver('fc-state-hover');
18766 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18767 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18768 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18769 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18771 this.on('monthchange', this.onMonthChange, this);
18773 this.update(new Date().clearTime());
18776 resize : function() {
18777 var sz = this.el.getSize();
18779 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18780 this.el.select('.fc-day-content div',true).setHeight(34);
18785 showPrevMonth : function(e){
18786 this.update(this.activeDate.add("mo", -1));
18788 showToday : function(e){
18789 this.update(new Date().clearTime());
18792 showNextMonth : function(e){
18793 this.update(this.activeDate.add("mo", 1));
18797 showPrevYear : function(){
18798 this.update(this.activeDate.add("y", -1));
18802 showNextYear : function(){
18803 this.update(this.activeDate.add("y", 1));
18808 update : function(date)
18810 var vd = this.activeDate;
18811 this.activeDate = date;
18812 // if(vd && this.el){
18813 // var t = date.getTime();
18814 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18815 // Roo.log('using add remove');
18817 // this.fireEvent('monthchange', this, date);
18819 // this.cells.removeClass("fc-state-highlight");
18820 // this.cells.each(function(c){
18821 // if(c.dateValue == t){
18822 // c.addClass("fc-state-highlight");
18823 // setTimeout(function(){
18824 // try{c.dom.firstChild.focus();}catch(e){}
18834 var days = date.getDaysInMonth();
18836 var firstOfMonth = date.getFirstDateOfMonth();
18837 var startingPos = firstOfMonth.getDay()-this.startDay;
18839 if(startingPos < this.startDay){
18843 var pm = date.add(Date.MONTH, -1);
18844 var prevStart = pm.getDaysInMonth()-startingPos;
18846 this.cells = this.el.select('.fc-day',true);
18847 this.textNodes = this.el.query('.fc-day-number');
18848 this.cells.addClassOnOver('fc-state-hover');
18850 var cells = this.cells.elements;
18851 var textEls = this.textNodes;
18853 Roo.each(cells, function(cell){
18854 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18857 days += startingPos;
18859 // convert everything to numbers so it's fast
18860 var day = 86400000;
18861 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18864 //Roo.log(prevStart);
18866 var today = new Date().clearTime().getTime();
18867 var sel = date.clearTime().getTime();
18868 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18869 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18870 var ddMatch = this.disabledDatesRE;
18871 var ddText = this.disabledDatesText;
18872 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18873 var ddaysText = this.disabledDaysText;
18874 var format = this.format;
18876 var setCellClass = function(cal, cell){
18880 //Roo.log('set Cell Class');
18882 var t = d.getTime();
18886 cell.dateValue = t;
18888 cell.className += " fc-today";
18889 cell.className += " fc-state-highlight";
18890 cell.title = cal.todayText;
18893 // disable highlight in other month..
18894 //cell.className += " fc-state-highlight";
18899 cell.className = " fc-state-disabled";
18900 cell.title = cal.minText;
18904 cell.className = " fc-state-disabled";
18905 cell.title = cal.maxText;
18909 if(ddays.indexOf(d.getDay()) != -1){
18910 cell.title = ddaysText;
18911 cell.className = " fc-state-disabled";
18914 if(ddMatch && format){
18915 var fvalue = d.dateFormat(format);
18916 if(ddMatch.test(fvalue)){
18917 cell.title = ddText.replace("%0", fvalue);
18918 cell.className = " fc-state-disabled";
18922 if (!cell.initialClassName) {
18923 cell.initialClassName = cell.dom.className;
18926 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18931 for(; i < startingPos; i++) {
18932 textEls[i].innerHTML = (++prevStart);
18933 d.setDate(d.getDate()+1);
18935 cells[i].className = "fc-past fc-other-month";
18936 setCellClass(this, cells[i]);
18941 for(; i < days; i++){
18942 intDay = i - startingPos + 1;
18943 textEls[i].innerHTML = (intDay);
18944 d.setDate(d.getDate()+1);
18946 cells[i].className = ''; // "x-date-active";
18947 setCellClass(this, cells[i]);
18951 for(; i < 42; i++) {
18952 textEls[i].innerHTML = (++extraDays);
18953 d.setDate(d.getDate()+1);
18955 cells[i].className = "fc-future fc-other-month";
18956 setCellClass(this, cells[i]);
18959 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18961 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18963 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18964 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18966 if(totalRows != 6){
18967 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18968 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18971 this.fireEvent('monthchange', this, date);
18975 if(!this.internalRender){
18976 var main = this.el.dom.firstChild;
18977 var w = main.offsetWidth;
18978 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18979 Roo.fly(main).setWidth(w);
18980 this.internalRender = true;
18981 // opera does not respect the auto grow header center column
18982 // then, after it gets a width opera refuses to recalculate
18983 // without a second pass
18984 if(Roo.isOpera && !this.secondPass){
18985 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18986 this.secondPass = true;
18987 this.update.defer(10, this, [date]);
18994 findCell : function(dt) {
18995 dt = dt.clearTime().getTime();
18997 this.cells.each(function(c){
18998 //Roo.log("check " +c.dateValue + '?=' + dt);
18999 if(c.dateValue == dt){
19009 findCells : function(ev) {
19010 var s = ev.start.clone().clearTime().getTime();
19012 var e= ev.end.clone().clearTime().getTime();
19015 this.cells.each(function(c){
19016 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19018 if(c.dateValue > e){
19021 if(c.dateValue < s){
19030 // findBestRow: function(cells)
19034 // for (var i =0 ; i < cells.length;i++) {
19035 // ret = Math.max(cells[i].rows || 0,ret);
19042 addItem : function(ev)
19044 // look for vertical location slot in
19045 var cells = this.findCells(ev);
19047 // ev.row = this.findBestRow(cells);
19049 // work out the location.
19053 for(var i =0; i < cells.length; i++) {
19055 cells[i].row = cells[0].row;
19058 cells[i].row = cells[i].row + 1;
19068 if (crow.start.getY() == cells[i].getY()) {
19070 crow.end = cells[i];
19087 cells[0].events.push(ev);
19089 this.calevents.push(ev);
19092 clearEvents: function() {
19094 if(!this.calevents){
19098 Roo.each(this.cells.elements, function(c){
19104 Roo.each(this.calevents, function(e) {
19105 Roo.each(e.els, function(el) {
19106 el.un('mouseenter' ,this.onEventEnter, this);
19107 el.un('mouseleave' ,this.onEventLeave, this);
19112 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19118 renderEvents: function()
19122 this.cells.each(function(c) {
19131 if(c.row != c.events.length){
19132 r = 4 - (4 - (c.row - c.events.length));
19135 c.events = ev.slice(0, r);
19136 c.more = ev.slice(r);
19138 if(c.more.length && c.more.length == 1){
19139 c.events.push(c.more.pop());
19142 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19146 this.cells.each(function(c) {
19148 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19151 for (var e = 0; e < c.events.length; e++){
19152 var ev = c.events[e];
19153 var rows = ev.rows;
19155 for(var i = 0; i < rows.length; i++) {
19157 // how many rows should it span..
19160 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19161 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19163 unselectable : "on",
19166 cls: 'fc-event-inner',
19170 // cls: 'fc-event-time',
19171 // html : cells.length > 1 ? '' : ev.time
19175 cls: 'fc-event-title',
19176 html : String.format('{0}', ev.title)
19183 cls: 'ui-resizable-handle ui-resizable-e',
19184 html : '  '
19191 cfg.cls += ' fc-event-start';
19193 if ((i+1) == rows.length) {
19194 cfg.cls += ' fc-event-end';
19197 var ctr = _this.el.select('.fc-event-container',true).first();
19198 var cg = ctr.createChild(cfg);
19200 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19201 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19203 var r = (c.more.length) ? 1 : 0;
19204 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19205 cg.setWidth(ebox.right - sbox.x -2);
19207 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19208 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19209 cg.on('click', _this.onEventClick, _this, ev);
19220 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19221 style : 'position: absolute',
19222 unselectable : "on",
19225 cls: 'fc-event-inner',
19229 cls: 'fc-event-title',
19237 cls: 'ui-resizable-handle ui-resizable-e',
19238 html : '  '
19244 var ctr = _this.el.select('.fc-event-container',true).first();
19245 var cg = ctr.createChild(cfg);
19247 var sbox = c.select('.fc-day-content',true).first().getBox();
19248 var ebox = c.select('.fc-day-content',true).first().getBox();
19250 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19251 cg.setWidth(ebox.right - sbox.x -2);
19253 cg.on('click', _this.onMoreEventClick, _this, c.more);
19263 onEventEnter: function (e, el,event,d) {
19264 this.fireEvent('evententer', this, el, event);
19267 onEventLeave: function (e, el,event,d) {
19268 this.fireEvent('eventleave', this, el, event);
19271 onEventClick: function (e, el,event,d) {
19272 this.fireEvent('eventclick', this, el, event);
19275 onMonthChange: function () {
19279 onMoreEventClick: function(e, el, more)
19283 this.calpopover.placement = 'right';
19284 this.calpopover.setTitle('More');
19286 this.calpopover.setContent('');
19288 var ctr = this.calpopover.el.select('.popover-content', true).first();
19290 Roo.each(more, function(m){
19292 cls : 'fc-event-hori fc-event-draggable',
19295 var cg = ctr.createChild(cfg);
19297 cg.on('click', _this.onEventClick, _this, m);
19300 this.calpopover.show(el);
19305 onLoad: function ()
19307 this.calevents = [];
19310 if(this.store.getCount() > 0){
19311 this.store.data.each(function(d){
19314 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19315 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19316 time : d.data.start_time,
19317 title : d.data.title,
19318 description : d.data.description,
19319 venue : d.data.venue
19324 this.renderEvents();
19326 if(this.calevents.length && this.loadMask){
19327 this.maskEl.hide();
19331 onBeforeLoad: function()
19333 this.clearEvents();
19335 this.maskEl.show();
19349 * @class Roo.bootstrap.Popover
19350 * @extends Roo.bootstrap.Component
19351 * Bootstrap Popover class
19352 * @cfg {String} html contents of the popover (or false to use children..)
19353 * @cfg {String} title of popover (or false to hide)
19354 * @cfg {String} placement how it is placed
19355 * @cfg {String} trigger click || hover (or false to trigger manually)
19356 * @cfg {String} over what (parent or false to trigger manually.)
19357 * @cfg {Number} delay - delay before showing
19360 * Create a new Popover
19361 * @param {Object} config The config object
19364 Roo.bootstrap.Popover = function(config){
19365 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19371 * After the popover show
19373 * @param {Roo.bootstrap.Popover} this
19378 * After the popover hide
19380 * @param {Roo.bootstrap.Popover} this
19386 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19388 title: 'Fill in a title',
19391 placement : 'right',
19392 trigger : 'hover', // hover
19398 can_build_overlaid : false,
19400 getChildContainer : function()
19402 return this.el.select('.popover-content',true).first();
19405 getAutoCreate : function(){
19408 cls : 'popover roo-dynamic',
19409 style: 'display:block',
19415 cls : 'popover-inner',
19419 cls: 'popover-title popover-header',
19423 cls : 'popover-content popover-body',
19434 setTitle: function(str)
19437 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19439 setContent: function(str)
19442 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19444 // as it get's added to the bottom of the page.
19445 onRender : function(ct, position)
19447 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19449 var cfg = Roo.apply({}, this.getAutoCreate());
19453 cfg.cls += ' ' + this.cls;
19456 cfg.style = this.style;
19458 //Roo.log("adding to ");
19459 this.el = Roo.get(document.body).createChild(cfg, position);
19460 // Roo.log(this.el);
19465 initEvents : function()
19467 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19468 this.el.enableDisplayMode('block');
19470 if (this.over === false) {
19473 if (this.triggers === false) {
19476 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19477 var triggers = this.trigger ? this.trigger.split(' ') : [];
19478 Roo.each(triggers, function(trigger) {
19480 if (trigger == 'click') {
19481 on_el.on('click', this.toggle, this);
19482 } else if (trigger != 'manual') {
19483 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19484 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19486 on_el.on(eventIn ,this.enter, this);
19487 on_el.on(eventOut, this.leave, this);
19498 toggle : function () {
19499 this.hoverState == 'in' ? this.leave() : this.enter();
19502 enter : function () {
19504 clearTimeout(this.timeout);
19506 this.hoverState = 'in';
19508 if (!this.delay || !this.delay.show) {
19513 this.timeout = setTimeout(function () {
19514 if (_t.hoverState == 'in') {
19517 }, this.delay.show)
19520 leave : function() {
19521 clearTimeout(this.timeout);
19523 this.hoverState = 'out';
19525 if (!this.delay || !this.delay.hide) {
19530 this.timeout = setTimeout(function () {
19531 if (_t.hoverState == 'out') {
19534 }, this.delay.hide)
19537 show : function (on_el)
19540 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19544 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19545 if (this.html !== false) {
19546 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19548 this.el.removeClass([
19549 'fade','top','bottom', 'left', 'right','in',
19550 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19552 if (!this.title.length) {
19553 this.el.select('.popover-title',true).hide();
19556 var placement = typeof this.placement == 'function' ?
19557 this.placement.call(this, this.el, on_el) :
19560 var autoToken = /\s?auto?\s?/i;
19561 var autoPlace = autoToken.test(placement);
19563 placement = placement.replace(autoToken, '') || 'top';
19567 //this.el.setXY([0,0]);
19569 this.el.dom.style.display='block';
19570 this.el.addClass(placement);
19572 //this.el.appendTo(on_el);
19574 var p = this.getPosition();
19575 var box = this.el.getBox();
19580 var align = Roo.bootstrap.Popover.alignment[placement];
19583 this.el.alignTo(on_el, align[0],align[1]);
19584 //var arrow = this.el.select('.arrow',true).first();
19585 //arrow.set(align[2],
19587 this.el.addClass('in');
19590 if (this.el.hasClass('fade')) {
19594 this.hoverState = 'in';
19596 this.fireEvent('show', this);
19601 this.el.setXY([0,0]);
19602 this.el.removeClass('in');
19604 this.hoverState = null;
19606 this.fireEvent('hide', this);
19611 Roo.bootstrap.Popover.alignment = {
19612 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19613 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19614 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19615 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19626 * @class Roo.bootstrap.Progress
19627 * @extends Roo.bootstrap.Component
19628 * Bootstrap Progress class
19629 * @cfg {Boolean} striped striped of the progress bar
19630 * @cfg {Boolean} active animated of the progress bar
19634 * Create a new Progress
19635 * @param {Object} config The config object
19638 Roo.bootstrap.Progress = function(config){
19639 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19642 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19647 getAutoCreate : function(){
19655 cfg.cls += ' progress-striped';
19659 cfg.cls += ' active';
19678 * @class Roo.bootstrap.ProgressBar
19679 * @extends Roo.bootstrap.Component
19680 * Bootstrap ProgressBar class
19681 * @cfg {Number} aria_valuenow aria-value now
19682 * @cfg {Number} aria_valuemin aria-value min
19683 * @cfg {Number} aria_valuemax aria-value max
19684 * @cfg {String} label label for the progress bar
19685 * @cfg {String} panel (success | info | warning | danger )
19686 * @cfg {String} role role of the progress bar
19687 * @cfg {String} sr_only text
19691 * Create a new ProgressBar
19692 * @param {Object} config The config object
19695 Roo.bootstrap.ProgressBar = function(config){
19696 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19699 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19703 aria_valuemax : 100,
19709 getAutoCreate : function()
19714 cls: 'progress-bar',
19715 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19727 cfg.role = this.role;
19730 if(this.aria_valuenow){
19731 cfg['aria-valuenow'] = this.aria_valuenow;
19734 if(this.aria_valuemin){
19735 cfg['aria-valuemin'] = this.aria_valuemin;
19738 if(this.aria_valuemax){
19739 cfg['aria-valuemax'] = this.aria_valuemax;
19742 if(this.label && !this.sr_only){
19743 cfg.html = this.label;
19747 cfg.cls += ' progress-bar-' + this.panel;
19753 update : function(aria_valuenow)
19755 this.aria_valuenow = aria_valuenow;
19757 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19772 * @class Roo.bootstrap.TabGroup
19773 * @extends Roo.bootstrap.Column
19774 * Bootstrap Column class
19775 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19776 * @cfg {Boolean} carousel true to make the group behave like a carousel
19777 * @cfg {Boolean} bullets show bullets for the panels
19778 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19779 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19780 * @cfg {Boolean} showarrow (true|false) show arrow default true
19783 * Create a new TabGroup
19784 * @param {Object} config The config object
19787 Roo.bootstrap.TabGroup = function(config){
19788 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19790 this.navId = Roo.id();
19793 Roo.bootstrap.TabGroup.register(this);
19797 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19800 transition : false,
19805 slideOnTouch : false,
19808 getAutoCreate : function()
19810 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19812 cfg.cls += ' tab-content';
19814 if (this.carousel) {
19815 cfg.cls += ' carousel slide';
19818 cls : 'carousel-inner',
19822 if(this.bullets && !Roo.isTouch){
19825 cls : 'carousel-bullets',
19829 if(this.bullets_cls){
19830 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19837 cfg.cn[0].cn.push(bullets);
19840 if(this.showarrow){
19841 cfg.cn[0].cn.push({
19843 class : 'carousel-arrow',
19847 class : 'carousel-prev',
19851 class : 'fa fa-chevron-left'
19857 class : 'carousel-next',
19861 class : 'fa fa-chevron-right'
19874 initEvents: function()
19876 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19877 // this.el.on("touchstart", this.onTouchStart, this);
19880 if(this.autoslide){
19883 this.slideFn = window.setInterval(function() {
19884 _this.showPanelNext();
19888 if(this.showarrow){
19889 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19890 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19896 // onTouchStart : function(e, el, o)
19898 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19902 // this.showPanelNext();
19906 getChildContainer : function()
19908 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19912 * register a Navigation item
19913 * @param {Roo.bootstrap.NavItem} the navitem to add
19915 register : function(item)
19917 this.tabs.push( item);
19918 item.navId = this.navId; // not really needed..
19923 getActivePanel : function()
19926 Roo.each(this.tabs, function(t) {
19936 getPanelByName : function(n)
19939 Roo.each(this.tabs, function(t) {
19940 if (t.tabId == n) {
19948 indexOfPanel : function(p)
19951 Roo.each(this.tabs, function(t,i) {
19952 if (t.tabId == p.tabId) {
19961 * show a specific panel
19962 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19963 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19965 showPanel : function (pan)
19967 if(this.transition || typeof(pan) == 'undefined'){
19968 Roo.log("waiting for the transitionend");
19972 if (typeof(pan) == 'number') {
19973 pan = this.tabs[pan];
19976 if (typeof(pan) == 'string') {
19977 pan = this.getPanelByName(pan);
19980 var cur = this.getActivePanel();
19983 Roo.log('pan or acitve pan is undefined');
19987 if (pan.tabId == this.getActivePanel().tabId) {
19991 if (false === cur.fireEvent('beforedeactivate')) {
19995 if(this.bullets > 0 && !Roo.isTouch){
19996 this.setActiveBullet(this.indexOfPanel(pan));
19999 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20001 //class="carousel-item carousel-item-next carousel-item-left"
20003 this.transition = true;
20004 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20005 var lr = dir == 'next' ? 'left' : 'right';
20006 pan.el.addClass(dir); // or prev
20007 pan.el.addClass('carousel-item-' + dir); // or prev
20008 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20009 cur.el.addClass(lr); // or right
20010 pan.el.addClass(lr);
20011 cur.el.addClass('carousel-item-' +lr); // or right
20012 pan.el.addClass('carousel-item-' +lr);
20016 cur.el.on('transitionend', function() {
20017 Roo.log("trans end?");
20019 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20020 pan.setActive(true);
20022 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20023 cur.setActive(false);
20025 _this.transition = false;
20027 }, this, { single: true } );
20032 cur.setActive(false);
20033 pan.setActive(true);
20038 showPanelNext : function()
20040 var i = this.indexOfPanel(this.getActivePanel());
20042 if (i >= this.tabs.length - 1 && !this.autoslide) {
20046 if (i >= this.tabs.length - 1 && this.autoslide) {
20050 this.showPanel(this.tabs[i+1]);
20053 showPanelPrev : function()
20055 var i = this.indexOfPanel(this.getActivePanel());
20057 if (i < 1 && !this.autoslide) {
20061 if (i < 1 && this.autoslide) {
20062 i = this.tabs.length;
20065 this.showPanel(this.tabs[i-1]);
20069 addBullet: function()
20071 if(!this.bullets || Roo.isTouch){
20074 var ctr = this.el.select('.carousel-bullets',true).first();
20075 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20076 var bullet = ctr.createChild({
20077 cls : 'bullet bullet-' + i
20078 },ctr.dom.lastChild);
20083 bullet.on('click', (function(e, el, o, ii, t){
20085 e.preventDefault();
20087 this.showPanel(ii);
20089 if(this.autoslide && this.slideFn){
20090 clearInterval(this.slideFn);
20091 this.slideFn = window.setInterval(function() {
20092 _this.showPanelNext();
20096 }).createDelegate(this, [i, bullet], true));
20101 setActiveBullet : function(i)
20107 Roo.each(this.el.select('.bullet', true).elements, function(el){
20108 el.removeClass('selected');
20111 var bullet = this.el.select('.bullet-' + i, true).first();
20117 bullet.addClass('selected');
20128 Roo.apply(Roo.bootstrap.TabGroup, {
20132 * register a Navigation Group
20133 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20135 register : function(navgrp)
20137 this.groups[navgrp.navId] = navgrp;
20141 * fetch a Navigation Group based on the navigation ID
20142 * if one does not exist , it will get created.
20143 * @param {string} the navgroup to add
20144 * @returns {Roo.bootstrap.NavGroup} the navgroup
20146 get: function(navId) {
20147 if (typeof(this.groups[navId]) == 'undefined') {
20148 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20150 return this.groups[navId] ;
20165 * @class Roo.bootstrap.TabPanel
20166 * @extends Roo.bootstrap.Component
20167 * Bootstrap TabPanel class
20168 * @cfg {Boolean} active panel active
20169 * @cfg {String} html panel content
20170 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20171 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20172 * @cfg {String} href click to link..
20176 * Create a new TabPanel
20177 * @param {Object} config The config object
20180 Roo.bootstrap.TabPanel = function(config){
20181 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20185 * Fires when the active status changes
20186 * @param {Roo.bootstrap.TabPanel} this
20187 * @param {Boolean} state the new state
20192 * @event beforedeactivate
20193 * Fires before a tab is de-activated - can be used to do validation on a form.
20194 * @param {Roo.bootstrap.TabPanel} this
20195 * @return {Boolean} false if there is an error
20198 'beforedeactivate': true
20201 this.tabId = this.tabId || Roo.id();
20205 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20213 getAutoCreate : function(){
20218 // item is needed for carousel - not sure if it has any effect otherwise
20219 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20220 html: this.html || ''
20224 cfg.cls += ' active';
20228 cfg.tabId = this.tabId;
20236 initEvents: function()
20238 var p = this.parent();
20240 this.navId = this.navId || p.navId;
20242 if (typeof(this.navId) != 'undefined') {
20243 // not really needed.. but just in case.. parent should be a NavGroup.
20244 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20248 var i = tg.tabs.length - 1;
20250 if(this.active && tg.bullets > 0 && i < tg.bullets){
20251 tg.setActiveBullet(i);
20255 this.el.on('click', this.onClick, this);
20258 this.el.on("touchstart", this.onTouchStart, this);
20259 this.el.on("touchmove", this.onTouchMove, this);
20260 this.el.on("touchend", this.onTouchEnd, this);
20265 onRender : function(ct, position)
20267 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20270 setActive : function(state)
20272 Roo.log("panel - set active " + this.tabId + "=" + state);
20274 this.active = state;
20276 this.el.removeClass('active');
20278 } else if (!this.el.hasClass('active')) {
20279 this.el.addClass('active');
20282 this.fireEvent('changed', this, state);
20285 onClick : function(e)
20287 e.preventDefault();
20289 if(!this.href.length){
20293 window.location.href = this.href;
20302 onTouchStart : function(e)
20304 this.swiping = false;
20306 this.startX = e.browserEvent.touches[0].clientX;
20307 this.startY = e.browserEvent.touches[0].clientY;
20310 onTouchMove : function(e)
20312 this.swiping = true;
20314 this.endX = e.browserEvent.touches[0].clientX;
20315 this.endY = e.browserEvent.touches[0].clientY;
20318 onTouchEnd : function(e)
20325 var tabGroup = this.parent();
20327 if(this.endX > this.startX){ // swiping right
20328 tabGroup.showPanelPrev();
20332 if(this.startX > this.endX){ // swiping left
20333 tabGroup.showPanelNext();
20352 * @class Roo.bootstrap.DateField
20353 * @extends Roo.bootstrap.Input
20354 * Bootstrap DateField class
20355 * @cfg {Number} weekStart default 0
20356 * @cfg {String} viewMode default empty, (months|years)
20357 * @cfg {String} minViewMode default empty, (months|years)
20358 * @cfg {Number} startDate default -Infinity
20359 * @cfg {Number} endDate default Infinity
20360 * @cfg {Boolean} todayHighlight default false
20361 * @cfg {Boolean} todayBtn default false
20362 * @cfg {Boolean} calendarWeeks default false
20363 * @cfg {Object} daysOfWeekDisabled default empty
20364 * @cfg {Boolean} singleMode default false (true | false)
20366 * @cfg {Boolean} keyboardNavigation default true
20367 * @cfg {String} language default en
20370 * Create a new DateField
20371 * @param {Object} config The config object
20374 Roo.bootstrap.DateField = function(config){
20375 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20379 * Fires when this field show.
20380 * @param {Roo.bootstrap.DateField} this
20381 * @param {Mixed} date The date value
20386 * Fires when this field hide.
20387 * @param {Roo.bootstrap.DateField} this
20388 * @param {Mixed} date The date value
20393 * Fires when select a date.
20394 * @param {Roo.bootstrap.DateField} this
20395 * @param {Mixed} date The date value
20399 * @event beforeselect
20400 * Fires when before select a date.
20401 * @param {Roo.bootstrap.DateField} this
20402 * @param {Mixed} date The date value
20404 beforeselect : true
20408 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20411 * @cfg {String} format
20412 * The default date format string which can be overriden for localization support. The format must be
20413 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20417 * @cfg {String} altFormats
20418 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20419 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20421 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20429 todayHighlight : false,
20435 keyboardNavigation: true,
20437 calendarWeeks: false,
20439 startDate: -Infinity,
20443 daysOfWeekDisabled: [],
20447 singleMode : false,
20449 UTCDate: function()
20451 return new Date(Date.UTC.apply(Date, arguments));
20454 UTCToday: function()
20456 var today = new Date();
20457 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20460 getDate: function() {
20461 var d = this.getUTCDate();
20462 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20465 getUTCDate: function() {
20469 setDate: function(d) {
20470 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20473 setUTCDate: function(d) {
20475 this.setValue(this.formatDate(this.date));
20478 onRender: function(ct, position)
20481 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20483 this.language = this.language || 'en';
20484 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20485 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20487 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20488 this.format = this.format || 'm/d/y';
20489 this.isInline = false;
20490 this.isInput = true;
20491 this.component = this.el.select('.add-on', true).first() || false;
20492 this.component = (this.component && this.component.length === 0) ? false : this.component;
20493 this.hasInput = this.component && this.inputEl().length;
20495 if (typeof(this.minViewMode === 'string')) {
20496 switch (this.minViewMode) {
20498 this.minViewMode = 1;
20501 this.minViewMode = 2;
20504 this.minViewMode = 0;
20509 if (typeof(this.viewMode === 'string')) {
20510 switch (this.viewMode) {
20523 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20525 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20527 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20529 this.picker().on('mousedown', this.onMousedown, this);
20530 this.picker().on('click', this.onClick, this);
20532 this.picker().addClass('datepicker-dropdown');
20534 this.startViewMode = this.viewMode;
20536 if(this.singleMode){
20537 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20538 v.setVisibilityMode(Roo.Element.DISPLAY);
20542 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20543 v.setStyle('width', '189px');
20547 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20548 if(!this.calendarWeeks){
20553 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20554 v.attr('colspan', function(i, val){
20555 return parseInt(val) + 1;
20560 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20562 this.setStartDate(this.startDate);
20563 this.setEndDate(this.endDate);
20565 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20572 if(this.isInline) {
20577 picker : function()
20579 return this.pickerEl;
20580 // return this.el.select('.datepicker', true).first();
20583 fillDow: function()
20585 var dowCnt = this.weekStart;
20594 if(this.calendarWeeks){
20602 while (dowCnt < this.weekStart + 7) {
20606 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20610 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20613 fillMonths: function()
20616 var months = this.picker().select('>.datepicker-months td', true).first();
20618 months.dom.innerHTML = '';
20624 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20627 months.createChild(month);
20634 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;
20636 if (this.date < this.startDate) {
20637 this.viewDate = new Date(this.startDate);
20638 } else if (this.date > this.endDate) {
20639 this.viewDate = new Date(this.endDate);
20641 this.viewDate = new Date(this.date);
20649 var d = new Date(this.viewDate),
20650 year = d.getUTCFullYear(),
20651 month = d.getUTCMonth(),
20652 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20653 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20654 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20655 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20656 currentDate = this.date && this.date.valueOf(),
20657 today = this.UTCToday();
20659 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20661 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20663 // this.picker.select('>tfoot th.today').
20664 // .text(dates[this.language].today)
20665 // .toggle(this.todayBtn !== false);
20667 this.updateNavArrows();
20670 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20672 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20674 prevMonth.setUTCDate(day);
20676 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20678 var nextMonth = new Date(prevMonth);
20680 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20682 nextMonth = nextMonth.valueOf();
20684 var fillMonths = false;
20686 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20688 while(prevMonth.valueOf() <= nextMonth) {
20691 if (prevMonth.getUTCDay() === this.weekStart) {
20693 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20701 if(this.calendarWeeks){
20702 // ISO 8601: First week contains first thursday.
20703 // ISO also states week starts on Monday, but we can be more abstract here.
20705 // Start of current week: based on weekstart/current date
20706 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20707 // Thursday of this week
20708 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20709 // First Thursday of year, year from thursday
20710 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20711 // Calendar week: ms between thursdays, div ms per day, div 7 days
20712 calWeek = (th - yth) / 864e5 / 7 + 1;
20714 fillMonths.cn.push({
20722 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20724 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20727 if (this.todayHighlight &&
20728 prevMonth.getUTCFullYear() == today.getFullYear() &&
20729 prevMonth.getUTCMonth() == today.getMonth() &&
20730 prevMonth.getUTCDate() == today.getDate()) {
20731 clsName += ' today';
20734 if (currentDate && prevMonth.valueOf() === currentDate) {
20735 clsName += ' active';
20738 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20739 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20740 clsName += ' disabled';
20743 fillMonths.cn.push({
20745 cls: 'day ' + clsName,
20746 html: prevMonth.getDate()
20749 prevMonth.setDate(prevMonth.getDate()+1);
20752 var currentYear = this.date && this.date.getUTCFullYear();
20753 var currentMonth = this.date && this.date.getUTCMonth();
20755 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20757 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20758 v.removeClass('active');
20760 if(currentYear === year && k === currentMonth){
20761 v.addClass('active');
20764 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20765 v.addClass('disabled');
20771 year = parseInt(year/10, 10) * 10;
20773 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20775 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20778 for (var i = -1; i < 11; i++) {
20779 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20781 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20789 showMode: function(dir)
20792 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20795 Roo.each(this.picker().select('>div',true).elements, function(v){
20796 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20799 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20804 if(this.isInline) {
20808 this.picker().removeClass(['bottom', 'top']);
20810 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20812 * place to the top of element!
20816 this.picker().addClass('top');
20817 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20822 this.picker().addClass('bottom');
20824 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20827 parseDate : function(value)
20829 if(!value || value instanceof Date){
20832 var v = Date.parseDate(value, this.format);
20833 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20834 v = Date.parseDate(value, 'Y-m-d');
20836 if(!v && this.altFormats){
20837 if(!this.altFormatsArray){
20838 this.altFormatsArray = this.altFormats.split("|");
20840 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20841 v = Date.parseDate(value, this.altFormatsArray[i]);
20847 formatDate : function(date, fmt)
20849 return (!date || !(date instanceof Date)) ?
20850 date : date.dateFormat(fmt || this.format);
20853 onFocus : function()
20855 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20859 onBlur : function()
20861 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20863 var d = this.inputEl().getValue();
20870 showPopup : function()
20872 this.picker().show();
20876 this.fireEvent('showpopup', this, this.date);
20879 hidePopup : function()
20881 if(this.isInline) {
20884 this.picker().hide();
20885 this.viewMode = this.startViewMode;
20888 this.fireEvent('hidepopup', this, this.date);
20892 onMousedown: function(e)
20894 e.stopPropagation();
20895 e.preventDefault();
20900 Roo.bootstrap.DateField.superclass.keyup.call(this);
20904 setValue: function(v)
20906 if(this.fireEvent('beforeselect', this, v) !== false){
20907 var d = new Date(this.parseDate(v) ).clearTime();
20909 if(isNaN(d.getTime())){
20910 this.date = this.viewDate = '';
20911 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20915 v = this.formatDate(d);
20917 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20919 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20923 this.fireEvent('select', this, this.date);
20927 getValue: function()
20929 return this.formatDate(this.date);
20932 fireKey: function(e)
20934 if (!this.picker().isVisible()){
20935 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20941 var dateChanged = false,
20943 newDate, newViewDate;
20948 e.preventDefault();
20952 if (!this.keyboardNavigation) {
20955 dir = e.keyCode == 37 ? -1 : 1;
20958 newDate = this.moveYear(this.date, dir);
20959 newViewDate = this.moveYear(this.viewDate, dir);
20960 } else if (e.shiftKey){
20961 newDate = this.moveMonth(this.date, dir);
20962 newViewDate = this.moveMonth(this.viewDate, dir);
20964 newDate = new Date(this.date);
20965 newDate.setUTCDate(this.date.getUTCDate() + dir);
20966 newViewDate = new Date(this.viewDate);
20967 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20969 if (this.dateWithinRange(newDate)){
20970 this.date = newDate;
20971 this.viewDate = newViewDate;
20972 this.setValue(this.formatDate(this.date));
20974 e.preventDefault();
20975 dateChanged = true;
20980 if (!this.keyboardNavigation) {
20983 dir = e.keyCode == 38 ? -1 : 1;
20985 newDate = this.moveYear(this.date, dir);
20986 newViewDate = this.moveYear(this.viewDate, dir);
20987 } else if (e.shiftKey){
20988 newDate = this.moveMonth(this.date, dir);
20989 newViewDate = this.moveMonth(this.viewDate, dir);
20991 newDate = new Date(this.date);
20992 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20993 newViewDate = new Date(this.viewDate);
20994 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20996 if (this.dateWithinRange(newDate)){
20997 this.date = newDate;
20998 this.viewDate = newViewDate;
20999 this.setValue(this.formatDate(this.date));
21001 e.preventDefault();
21002 dateChanged = true;
21006 this.setValue(this.formatDate(this.date));
21008 e.preventDefault();
21011 this.setValue(this.formatDate(this.date));
21025 onClick: function(e)
21027 e.stopPropagation();
21028 e.preventDefault();
21030 var target = e.getTarget();
21032 if(target.nodeName.toLowerCase() === 'i'){
21033 target = Roo.get(target).dom.parentNode;
21036 var nodeName = target.nodeName;
21037 var className = target.className;
21038 var html = target.innerHTML;
21039 //Roo.log(nodeName);
21041 switch(nodeName.toLowerCase()) {
21043 switch(className) {
21049 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21050 switch(this.viewMode){
21052 this.viewDate = this.moveMonth(this.viewDate, dir);
21056 this.viewDate = this.moveYear(this.viewDate, dir);
21062 var date = new Date();
21063 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21065 this.setValue(this.formatDate(this.date));
21072 if (className.indexOf('disabled') < 0) {
21073 this.viewDate.setUTCDate(1);
21074 if (className.indexOf('month') > -1) {
21075 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21077 var year = parseInt(html, 10) || 0;
21078 this.viewDate.setUTCFullYear(year);
21082 if(this.singleMode){
21083 this.setValue(this.formatDate(this.viewDate));
21094 //Roo.log(className);
21095 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21096 var day = parseInt(html, 10) || 1;
21097 var year = this.viewDate.getUTCFullYear(),
21098 month = this.viewDate.getUTCMonth();
21100 if (className.indexOf('old') > -1) {
21107 } else if (className.indexOf('new') > -1) {
21115 //Roo.log([year,month,day]);
21116 this.date = this.UTCDate(year, month, day,0,0,0,0);
21117 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21119 //Roo.log(this.formatDate(this.date));
21120 this.setValue(this.formatDate(this.date));
21127 setStartDate: function(startDate)
21129 this.startDate = startDate || -Infinity;
21130 if (this.startDate !== -Infinity) {
21131 this.startDate = this.parseDate(this.startDate);
21134 this.updateNavArrows();
21137 setEndDate: function(endDate)
21139 this.endDate = endDate || Infinity;
21140 if (this.endDate !== Infinity) {
21141 this.endDate = this.parseDate(this.endDate);
21144 this.updateNavArrows();
21147 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21149 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21150 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21151 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21153 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21154 return parseInt(d, 10);
21157 this.updateNavArrows();
21160 updateNavArrows: function()
21162 if(this.singleMode){
21166 var d = new Date(this.viewDate),
21167 year = d.getUTCFullYear(),
21168 month = d.getUTCMonth();
21170 Roo.each(this.picker().select('.prev', true).elements, function(v){
21172 switch (this.viewMode) {
21175 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21181 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21188 Roo.each(this.picker().select('.next', true).elements, function(v){
21190 switch (this.viewMode) {
21193 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21199 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21207 moveMonth: function(date, dir)
21212 var new_date = new Date(date.valueOf()),
21213 day = new_date.getUTCDate(),
21214 month = new_date.getUTCMonth(),
21215 mag = Math.abs(dir),
21217 dir = dir > 0 ? 1 : -1;
21220 // If going back one month, make sure month is not current month
21221 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21223 return new_date.getUTCMonth() == month;
21225 // If going forward one month, make sure month is as expected
21226 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21228 return new_date.getUTCMonth() != new_month;
21230 new_month = month + dir;
21231 new_date.setUTCMonth(new_month);
21232 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21233 if (new_month < 0 || new_month > 11) {
21234 new_month = (new_month + 12) % 12;
21237 // For magnitudes >1, move one month at a time...
21238 for (var i=0; i<mag; i++) {
21239 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21240 new_date = this.moveMonth(new_date, dir);
21242 // ...then reset the day, keeping it in the new month
21243 new_month = new_date.getUTCMonth();
21244 new_date.setUTCDate(day);
21246 return new_month != new_date.getUTCMonth();
21249 // Common date-resetting loop -- if date is beyond end of month, make it
21252 new_date.setUTCDate(--day);
21253 new_date.setUTCMonth(new_month);
21258 moveYear: function(date, dir)
21260 return this.moveMonth(date, dir*12);
21263 dateWithinRange: function(date)
21265 return date >= this.startDate && date <= this.endDate;
21271 this.picker().remove();
21274 validateValue : function(value)
21276 if(this.getVisibilityEl().hasClass('hidden')){
21280 if(value.length < 1) {
21281 if(this.allowBlank){
21287 if(value.length < this.minLength){
21290 if(value.length > this.maxLength){
21294 var vt = Roo.form.VTypes;
21295 if(!vt[this.vtype](value, this)){
21299 if(typeof this.validator == "function"){
21300 var msg = this.validator(value);
21306 if(this.regex && !this.regex.test(value)){
21310 if(typeof(this.parseDate(value)) == 'undefined'){
21314 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21318 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21328 this.date = this.viewDate = '';
21330 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21335 Roo.apply(Roo.bootstrap.DateField, {
21346 html: '<i class="fa fa-arrow-left"/>'
21356 html: '<i class="fa fa-arrow-right"/>'
21398 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21399 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21400 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21401 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21402 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21415 navFnc: 'FullYear',
21420 navFnc: 'FullYear',
21425 Roo.apply(Roo.bootstrap.DateField, {
21429 cls: 'datepicker dropdown-menu roo-dynamic',
21433 cls: 'datepicker-days',
21437 cls: 'table-condensed',
21439 Roo.bootstrap.DateField.head,
21443 Roo.bootstrap.DateField.footer
21450 cls: 'datepicker-months',
21454 cls: 'table-condensed',
21456 Roo.bootstrap.DateField.head,
21457 Roo.bootstrap.DateField.content,
21458 Roo.bootstrap.DateField.footer
21465 cls: 'datepicker-years',
21469 cls: 'table-condensed',
21471 Roo.bootstrap.DateField.head,
21472 Roo.bootstrap.DateField.content,
21473 Roo.bootstrap.DateField.footer
21492 * @class Roo.bootstrap.TimeField
21493 * @extends Roo.bootstrap.Input
21494 * Bootstrap DateField class
21498 * Create a new TimeField
21499 * @param {Object} config The config object
21502 Roo.bootstrap.TimeField = function(config){
21503 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21507 * Fires when this field show.
21508 * @param {Roo.bootstrap.DateField} thisthis
21509 * @param {Mixed} date The date value
21514 * Fires when this field hide.
21515 * @param {Roo.bootstrap.DateField} this
21516 * @param {Mixed} date The date value
21521 * Fires when select a date.
21522 * @param {Roo.bootstrap.DateField} this
21523 * @param {Mixed} date The date value
21529 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21532 * @cfg {String} format
21533 * The default time format string which can be overriden for localization support. The format must be
21534 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21538 onRender: function(ct, position)
21541 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21543 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21545 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21547 this.pop = this.picker().select('>.datepicker-time',true).first();
21548 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21550 this.picker().on('mousedown', this.onMousedown, this);
21551 this.picker().on('click', this.onClick, this);
21553 this.picker().addClass('datepicker-dropdown');
21558 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21559 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21560 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21561 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21562 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21563 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21567 fireKey: function(e){
21568 if (!this.picker().isVisible()){
21569 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21575 e.preventDefault();
21583 this.onTogglePeriod();
21586 this.onIncrementMinutes();
21589 this.onDecrementMinutes();
21598 onClick: function(e) {
21599 e.stopPropagation();
21600 e.preventDefault();
21603 picker : function()
21605 return this.el.select('.datepicker', true).first();
21608 fillTime: function()
21610 var time = this.pop.select('tbody', true).first();
21612 time.dom.innerHTML = '';
21627 cls: 'hours-up glyphicon glyphicon-chevron-up'
21647 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21668 cls: 'timepicker-hour',
21683 cls: 'timepicker-minute',
21698 cls: 'btn btn-primary period',
21720 cls: 'hours-down glyphicon glyphicon-chevron-down'
21740 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21758 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21765 var hours = this.time.getHours();
21766 var minutes = this.time.getMinutes();
21779 hours = hours - 12;
21783 hours = '0' + hours;
21787 minutes = '0' + minutes;
21790 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21791 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21792 this.pop.select('button', true).first().dom.innerHTML = period;
21798 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21800 var cls = ['bottom'];
21802 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21809 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21814 this.picker().addClass(cls.join('-'));
21818 Roo.each(cls, function(c){
21820 _this.picker().setTop(_this.inputEl().getHeight());
21824 _this.picker().setTop(0 - _this.picker().getHeight());
21829 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21833 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21840 onFocus : function()
21842 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21846 onBlur : function()
21848 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21854 this.picker().show();
21859 this.fireEvent('show', this, this.date);
21864 this.picker().hide();
21867 this.fireEvent('hide', this, this.date);
21870 setTime : function()
21873 this.setValue(this.time.format(this.format));
21875 this.fireEvent('select', this, this.date);
21880 onMousedown: function(e){
21881 e.stopPropagation();
21882 e.preventDefault();
21885 onIncrementHours: function()
21887 Roo.log('onIncrementHours');
21888 this.time = this.time.add(Date.HOUR, 1);
21893 onDecrementHours: function()
21895 Roo.log('onDecrementHours');
21896 this.time = this.time.add(Date.HOUR, -1);
21900 onIncrementMinutes: function()
21902 Roo.log('onIncrementMinutes');
21903 this.time = this.time.add(Date.MINUTE, 1);
21907 onDecrementMinutes: function()
21909 Roo.log('onDecrementMinutes');
21910 this.time = this.time.add(Date.MINUTE, -1);
21914 onTogglePeriod: function()
21916 Roo.log('onTogglePeriod');
21917 this.time = this.time.add(Date.HOUR, 12);
21924 Roo.apply(Roo.bootstrap.TimeField, {
21954 cls: 'btn btn-info ok',
21966 Roo.apply(Roo.bootstrap.TimeField, {
21970 cls: 'datepicker dropdown-menu',
21974 cls: 'datepicker-time',
21978 cls: 'table-condensed',
21980 Roo.bootstrap.TimeField.content,
21981 Roo.bootstrap.TimeField.footer
22000 * @class Roo.bootstrap.MonthField
22001 * @extends Roo.bootstrap.Input
22002 * Bootstrap MonthField class
22004 * @cfg {String} language default en
22007 * Create a new MonthField
22008 * @param {Object} config The config object
22011 Roo.bootstrap.MonthField = function(config){
22012 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22017 * Fires when this field show.
22018 * @param {Roo.bootstrap.MonthField} this
22019 * @param {Mixed} date The date value
22024 * Fires when this field hide.
22025 * @param {Roo.bootstrap.MonthField} this
22026 * @param {Mixed} date The date value
22031 * Fires when select a date.
22032 * @param {Roo.bootstrap.MonthField} this
22033 * @param {String} oldvalue The old value
22034 * @param {String} newvalue The new value
22040 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22042 onRender: function(ct, position)
22045 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22047 this.language = this.language || 'en';
22048 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22049 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22051 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22052 this.isInline = false;
22053 this.isInput = true;
22054 this.component = this.el.select('.add-on', true).first() || false;
22055 this.component = (this.component && this.component.length === 0) ? false : this.component;
22056 this.hasInput = this.component && this.inputEL().length;
22058 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22060 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22062 this.picker().on('mousedown', this.onMousedown, this);
22063 this.picker().on('click', this.onClick, this);
22065 this.picker().addClass('datepicker-dropdown');
22067 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22068 v.setStyle('width', '189px');
22075 if(this.isInline) {
22081 setValue: function(v, suppressEvent)
22083 var o = this.getValue();
22085 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22089 if(suppressEvent !== true){
22090 this.fireEvent('select', this, o, v);
22095 getValue: function()
22100 onClick: function(e)
22102 e.stopPropagation();
22103 e.preventDefault();
22105 var target = e.getTarget();
22107 if(target.nodeName.toLowerCase() === 'i'){
22108 target = Roo.get(target).dom.parentNode;
22111 var nodeName = target.nodeName;
22112 var className = target.className;
22113 var html = target.innerHTML;
22115 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22119 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22121 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22127 picker : function()
22129 return this.pickerEl;
22132 fillMonths: function()
22135 var months = this.picker().select('>.datepicker-months td', true).first();
22137 months.dom.innerHTML = '';
22143 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22146 months.createChild(month);
22155 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22156 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22159 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22160 e.removeClass('active');
22162 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22163 e.addClass('active');
22170 if(this.isInline) {
22174 this.picker().removeClass(['bottom', 'top']);
22176 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22178 * place to the top of element!
22182 this.picker().addClass('top');
22183 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22188 this.picker().addClass('bottom');
22190 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22193 onFocus : function()
22195 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22199 onBlur : function()
22201 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22203 var d = this.inputEl().getValue();
22212 this.picker().show();
22213 this.picker().select('>.datepicker-months', true).first().show();
22217 this.fireEvent('show', this, this.date);
22222 if(this.isInline) {
22225 this.picker().hide();
22226 this.fireEvent('hide', this, this.date);
22230 onMousedown: function(e)
22232 e.stopPropagation();
22233 e.preventDefault();
22238 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22242 fireKey: function(e)
22244 if (!this.picker().isVisible()){
22245 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22256 e.preventDefault();
22260 dir = e.keyCode == 37 ? -1 : 1;
22262 this.vIndex = this.vIndex + dir;
22264 if(this.vIndex < 0){
22268 if(this.vIndex > 11){
22272 if(isNaN(this.vIndex)){
22276 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22282 dir = e.keyCode == 38 ? -1 : 1;
22284 this.vIndex = this.vIndex + dir * 4;
22286 if(this.vIndex < 0){
22290 if(this.vIndex > 11){
22294 if(isNaN(this.vIndex)){
22298 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22303 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22304 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22308 e.preventDefault();
22311 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22312 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22328 this.picker().remove();
22333 Roo.apply(Roo.bootstrap.MonthField, {
22352 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22353 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22358 Roo.apply(Roo.bootstrap.MonthField, {
22362 cls: 'datepicker dropdown-menu roo-dynamic',
22366 cls: 'datepicker-months',
22370 cls: 'table-condensed',
22372 Roo.bootstrap.DateField.content
22392 * @class Roo.bootstrap.CheckBox
22393 * @extends Roo.bootstrap.Input
22394 * Bootstrap CheckBox class
22396 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22397 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22398 * @cfg {String} boxLabel The text that appears beside the checkbox
22399 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22400 * @cfg {Boolean} checked initnal the element
22401 * @cfg {Boolean} inline inline the element (default false)
22402 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22403 * @cfg {String} tooltip label tooltip
22406 * Create a new CheckBox
22407 * @param {Object} config The config object
22410 Roo.bootstrap.CheckBox = function(config){
22411 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22416 * Fires when the element is checked or unchecked.
22417 * @param {Roo.bootstrap.CheckBox} this This input
22418 * @param {Boolean} checked The new checked value
22423 * Fires when the element is click.
22424 * @param {Roo.bootstrap.CheckBox} this This input
22431 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22433 inputType: 'checkbox',
22442 // checkbox success does not make any sense really..
22447 getAutoCreate : function()
22449 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22455 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22458 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22464 type : this.inputType,
22465 value : this.inputValue,
22466 cls : 'roo-' + this.inputType, //'form-box',
22467 placeholder : this.placeholder || ''
22471 if(this.inputType != 'radio'){
22475 cls : 'roo-hidden-value',
22476 value : this.checked ? this.inputValue : this.valueOff
22481 if (this.weight) { // Validity check?
22482 cfg.cls += " " + this.inputType + "-" + this.weight;
22485 if (this.disabled) {
22486 input.disabled=true;
22490 input.checked = this.checked;
22495 input.name = this.name;
22497 if(this.inputType != 'radio'){
22498 hidden.name = this.name;
22499 input.name = '_hidden_' + this.name;
22504 input.cls += ' input-' + this.size;
22509 ['xs','sm','md','lg'].map(function(size){
22510 if (settings[size]) {
22511 cfg.cls += ' col-' + size + '-' + settings[size];
22515 var inputblock = input;
22517 if (this.before || this.after) {
22520 cls : 'input-group',
22525 inputblock.cn.push({
22527 cls : 'input-group-addon',
22532 inputblock.cn.push(input);
22534 if(this.inputType != 'radio'){
22535 inputblock.cn.push(hidden);
22539 inputblock.cn.push({
22541 cls : 'input-group-addon',
22547 var boxLabelCfg = false;
22553 //'for': id, // box label is handled by onclick - so no for...
22555 html: this.boxLabel
22558 boxLabelCfg.tooltip = this.tooltip;
22564 if (align ==='left' && this.fieldLabel.length) {
22565 // Roo.log("left and has label");
22570 cls : 'control-label',
22571 html : this.fieldLabel
22582 cfg.cn[1].cn.push(boxLabelCfg);
22585 if(this.labelWidth > 12){
22586 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22589 if(this.labelWidth < 13 && this.labelmd == 0){
22590 this.labelmd = this.labelWidth;
22593 if(this.labellg > 0){
22594 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22595 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22598 if(this.labelmd > 0){
22599 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22600 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22603 if(this.labelsm > 0){
22604 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22605 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22608 if(this.labelxs > 0){
22609 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22610 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22613 } else if ( this.fieldLabel.length) {
22614 // Roo.log(" label");
22618 tag: this.boxLabel ? 'span' : 'label',
22620 cls: 'control-label box-input-label',
22621 //cls : 'input-group-addon',
22622 html : this.fieldLabel
22629 cfg.cn.push(boxLabelCfg);
22634 // Roo.log(" no label && no align");
22635 cfg.cn = [ inputblock ] ;
22637 cfg.cn.push(boxLabelCfg);
22645 if(this.inputType != 'radio'){
22646 cfg.cn.push(hidden);
22654 * return the real input element.
22656 inputEl: function ()
22658 return this.el.select('input.roo-' + this.inputType,true).first();
22660 hiddenEl: function ()
22662 return this.el.select('input.roo-hidden-value',true).first();
22665 labelEl: function()
22667 return this.el.select('label.control-label',true).first();
22669 /* depricated... */
22673 return this.labelEl();
22676 boxLabelEl: function()
22678 return this.el.select('label.box-label',true).first();
22681 initEvents : function()
22683 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22685 this.inputEl().on('click', this.onClick, this);
22687 if (this.boxLabel) {
22688 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22691 this.startValue = this.getValue();
22694 Roo.bootstrap.CheckBox.register(this);
22698 onClick : function(e)
22700 if(this.fireEvent('click', this, e) !== false){
22701 this.setChecked(!this.checked);
22706 setChecked : function(state,suppressEvent)
22708 this.startValue = this.getValue();
22710 if(this.inputType == 'radio'){
22712 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22713 e.dom.checked = false;
22716 this.inputEl().dom.checked = true;
22718 this.inputEl().dom.value = this.inputValue;
22720 if(suppressEvent !== true){
22721 this.fireEvent('check', this, true);
22729 this.checked = state;
22731 this.inputEl().dom.checked = state;
22734 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22736 if(suppressEvent !== true){
22737 this.fireEvent('check', this, state);
22743 getValue : function()
22745 if(this.inputType == 'radio'){
22746 return this.getGroupValue();
22749 return this.hiddenEl().dom.value;
22753 getGroupValue : function()
22755 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22759 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22762 setValue : function(v,suppressEvent)
22764 if(this.inputType == 'radio'){
22765 this.setGroupValue(v, suppressEvent);
22769 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22774 setGroupValue : function(v, suppressEvent)
22776 this.startValue = this.getValue();
22778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22779 e.dom.checked = false;
22781 if(e.dom.value == v){
22782 e.dom.checked = true;
22786 if(suppressEvent !== true){
22787 this.fireEvent('check', this, true);
22795 validate : function()
22797 if(this.getVisibilityEl().hasClass('hidden')){
22803 (this.inputType == 'radio' && this.validateRadio()) ||
22804 (this.inputType == 'checkbox' && this.validateCheckbox())
22810 this.markInvalid();
22814 validateRadio : function()
22816 if(this.getVisibilityEl().hasClass('hidden')){
22820 if(this.allowBlank){
22826 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22827 if(!e.dom.checked){
22839 validateCheckbox : function()
22842 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22843 //return (this.getValue() == this.inputValue) ? true : false;
22846 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22854 for(var i in group){
22855 if(group[i].el.isVisible(true)){
22863 for(var i in group){
22868 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22875 * Mark this field as valid
22877 markValid : function()
22881 this.fireEvent('valid', this);
22883 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22886 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22893 if(this.inputType == 'radio'){
22894 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22895 var fg = e.findParent('.form-group', false, true);
22896 if (Roo.bootstrap.version == 3) {
22897 fg.removeClass([_this.invalidClass, _this.validClass]);
22898 fg.addClass(_this.validClass);
22900 fg.removeClass(['is-valid', 'is-invalid']);
22901 fg.addClass('is-valid');
22909 var fg = this.el.findParent('.form-group', false, true);
22910 if (Roo.bootstrap.version == 3) {
22911 fg.removeClass([this.invalidClass, this.validClass]);
22912 fg.addClass(this.validClass);
22914 fg.removeClass(['is-valid', 'is-invalid']);
22915 fg.addClass('is-valid');
22920 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22926 for(var i in group){
22927 var fg = group[i].el.findParent('.form-group', false, true);
22928 if (Roo.bootstrap.version == 3) {
22929 fg.removeClass([this.invalidClass, this.validClass]);
22930 fg.addClass(this.validClass);
22932 fg.removeClass(['is-valid', 'is-invalid']);
22933 fg.addClass('is-valid');
22939 * Mark this field as invalid
22940 * @param {String} msg The validation message
22942 markInvalid : function(msg)
22944 if(this.allowBlank){
22950 this.fireEvent('invalid', this, msg);
22952 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22955 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22959 label.markInvalid();
22962 if(this.inputType == 'radio'){
22964 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22965 var fg = e.findParent('.form-group', false, true);
22966 if (Roo.bootstrap.version == 3) {
22967 fg.removeClass([_this.invalidClass, _this.validClass]);
22968 fg.addClass(_this.invalidClass);
22970 fg.removeClass(['is-invalid', 'is-valid']);
22971 fg.addClass('is-invalid');
22979 var fg = this.el.findParent('.form-group', false, true);
22980 if (Roo.bootstrap.version == 3) {
22981 fg.removeClass([_this.invalidClass, _this.validClass]);
22982 fg.addClass(_this.invalidClass);
22984 fg.removeClass(['is-invalid', 'is-valid']);
22985 fg.addClass('is-invalid');
22990 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22996 for(var i in group){
22997 var fg = group[i].el.findParent('.form-group', false, true);
22998 if (Roo.bootstrap.version == 3) {
22999 fg.removeClass([_this.invalidClass, _this.validClass]);
23000 fg.addClass(_this.invalidClass);
23002 fg.removeClass(['is-invalid', 'is-valid']);
23003 fg.addClass('is-invalid');
23009 clearInvalid : function()
23011 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23013 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23015 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23017 if (label && label.iconEl) {
23018 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23019 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23023 disable : function()
23025 if(this.inputType != 'radio'){
23026 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23033 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23034 _this.getActionEl().addClass(this.disabledClass);
23035 e.dom.disabled = true;
23039 this.disabled = true;
23040 this.fireEvent("disable", this);
23044 enable : function()
23046 if(this.inputType != 'radio'){
23047 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23054 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23055 _this.getActionEl().removeClass(this.disabledClass);
23056 e.dom.disabled = false;
23060 this.disabled = false;
23061 this.fireEvent("enable", this);
23065 setBoxLabel : function(v)
23070 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23076 Roo.apply(Roo.bootstrap.CheckBox, {
23081 * register a CheckBox Group
23082 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23084 register : function(checkbox)
23086 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23087 this.groups[checkbox.groupId] = {};
23090 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23094 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23098 * fetch a CheckBox Group based on the group ID
23099 * @param {string} the group ID
23100 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23102 get: function(groupId) {
23103 if (typeof(this.groups[groupId]) == 'undefined') {
23107 return this.groups[groupId] ;
23120 * @class Roo.bootstrap.Radio
23121 * @extends Roo.bootstrap.Component
23122 * Bootstrap Radio class
23123 * @cfg {String} boxLabel - the label associated
23124 * @cfg {String} value - the value of radio
23127 * Create a new Radio
23128 * @param {Object} config The config object
23130 Roo.bootstrap.Radio = function(config){
23131 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23135 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23141 getAutoCreate : function()
23145 cls : 'form-group radio',
23150 html : this.boxLabel
23158 initEvents : function()
23160 this.parent().register(this);
23162 this.el.on('click', this.onClick, this);
23166 onClick : function(e)
23168 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23169 this.setChecked(true);
23173 setChecked : function(state, suppressEvent)
23175 this.parent().setValue(this.value, suppressEvent);
23179 setBoxLabel : function(v)
23184 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23199 * @class Roo.bootstrap.SecurePass
23200 * @extends Roo.bootstrap.Input
23201 * Bootstrap SecurePass class
23205 * Create a new SecurePass
23206 * @param {Object} config The config object
23209 Roo.bootstrap.SecurePass = function (config) {
23210 // these go here, so the translation tool can replace them..
23212 PwdEmpty: "Please type a password, and then retype it to confirm.",
23213 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23214 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23215 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23216 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23217 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23218 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23219 TooWeak: "Your password is Too Weak."
23221 this.meterLabel = "Password strength:";
23222 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23223 this.meterClass = [
23224 "roo-password-meter-tooweak",
23225 "roo-password-meter-weak",
23226 "roo-password-meter-medium",
23227 "roo-password-meter-strong",
23228 "roo-password-meter-grey"
23233 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23236 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23238 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23240 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23241 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23242 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23243 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23244 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23245 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23246 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23256 * @cfg {String/Object} Label for the strength meter (defaults to
23257 * 'Password strength:')
23262 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23263 * ['Weak', 'Medium', 'Strong'])
23266 pwdStrengths: false,
23279 initEvents: function ()
23281 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23283 if (this.el.is('input[type=password]') && Roo.isSafari) {
23284 this.el.on('keydown', this.SafariOnKeyDown, this);
23287 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23290 onRender: function (ct, position)
23292 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23293 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23294 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23296 this.trigger.createChild({
23301 cls: 'roo-password-meter-grey col-xs-12',
23304 //width: this.meterWidth + 'px'
23308 cls: 'roo-password-meter-text'
23314 if (this.hideTrigger) {
23315 this.trigger.setDisplayed(false);
23317 this.setSize(this.width || '', this.height || '');
23320 onDestroy: function ()
23322 if (this.trigger) {
23323 this.trigger.removeAllListeners();
23324 this.trigger.remove();
23327 this.wrap.remove();
23329 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23332 checkStrength: function ()
23334 var pwd = this.inputEl().getValue();
23335 if (pwd == this._lastPwd) {
23340 if (this.ClientSideStrongPassword(pwd)) {
23342 } else if (this.ClientSideMediumPassword(pwd)) {
23344 } else if (this.ClientSideWeakPassword(pwd)) {
23350 Roo.log('strength1: ' + strength);
23352 //var pm = this.trigger.child('div/div/div').dom;
23353 var pm = this.trigger.child('div/div');
23354 pm.removeClass(this.meterClass);
23355 pm.addClass(this.meterClass[strength]);
23358 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23360 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23362 this._lastPwd = pwd;
23366 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23368 this._lastPwd = '';
23370 var pm = this.trigger.child('div/div');
23371 pm.removeClass(this.meterClass);
23372 pm.addClass('roo-password-meter-grey');
23375 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23378 this.inputEl().dom.type='password';
23381 validateValue: function (value)
23384 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23387 if (value.length == 0) {
23388 if (this.allowBlank) {
23389 this.clearInvalid();
23393 this.markInvalid(this.errors.PwdEmpty);
23394 this.errorMsg = this.errors.PwdEmpty;
23402 if ('[\x21-\x7e]*'.match(value)) {
23403 this.markInvalid(this.errors.PwdBadChar);
23404 this.errorMsg = this.errors.PwdBadChar;
23407 if (value.length < 6) {
23408 this.markInvalid(this.errors.PwdShort);
23409 this.errorMsg = this.errors.PwdShort;
23412 if (value.length > 16) {
23413 this.markInvalid(this.errors.PwdLong);
23414 this.errorMsg = this.errors.PwdLong;
23418 if (this.ClientSideStrongPassword(value)) {
23420 } else if (this.ClientSideMediumPassword(value)) {
23422 } else if (this.ClientSideWeakPassword(value)) {
23429 if (strength < 2) {
23430 //this.markInvalid(this.errors.TooWeak);
23431 this.errorMsg = this.errors.TooWeak;
23436 console.log('strength2: ' + strength);
23438 //var pm = this.trigger.child('div/div/div').dom;
23440 var pm = this.trigger.child('div/div');
23441 pm.removeClass(this.meterClass);
23442 pm.addClass(this.meterClass[strength]);
23444 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23446 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23448 this.errorMsg = '';
23452 CharacterSetChecks: function (type)
23455 this.fResult = false;
23458 isctype: function (character, type)
23461 case this.kCapitalLetter:
23462 if (character >= 'A' && character <= 'Z') {
23467 case this.kSmallLetter:
23468 if (character >= 'a' && character <= 'z') {
23474 if (character >= '0' && character <= '9') {
23479 case this.kPunctuation:
23480 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23491 IsLongEnough: function (pwd, size)
23493 return !(pwd == null || isNaN(size) || pwd.length < size);
23496 SpansEnoughCharacterSets: function (word, nb)
23498 if (!this.IsLongEnough(word, nb))
23503 var characterSetChecks = new Array(
23504 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23505 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23508 for (var index = 0; index < word.length; ++index) {
23509 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23510 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23511 characterSetChecks[nCharSet].fResult = true;
23518 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23519 if (characterSetChecks[nCharSet].fResult) {
23524 if (nCharSets < nb) {
23530 ClientSideStrongPassword: function (pwd)
23532 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23535 ClientSideMediumPassword: function (pwd)
23537 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23540 ClientSideWeakPassword: function (pwd)
23542 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23545 })//<script type="text/javascript">
23548 * Based Ext JS Library 1.1.1
23549 * Copyright(c) 2006-2007, Ext JS, LLC.
23555 * @class Roo.HtmlEditorCore
23556 * @extends Roo.Component
23557 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23559 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23562 Roo.HtmlEditorCore = function(config){
23565 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23570 * @event initialize
23571 * Fires when the editor is fully initialized (including the iframe)
23572 * @param {Roo.HtmlEditorCore} this
23577 * Fires when the editor is first receives the focus. Any insertion must wait
23578 * until after this event.
23579 * @param {Roo.HtmlEditorCore} this
23583 * @event beforesync
23584 * Fires before the textarea is updated with content from the editor iframe. Return false
23585 * to cancel the sync.
23586 * @param {Roo.HtmlEditorCore} this
23587 * @param {String} html
23591 * @event beforepush
23592 * Fires before the iframe editor is updated with content from the textarea. Return false
23593 * to cancel the push.
23594 * @param {Roo.HtmlEditorCore} this
23595 * @param {String} html
23600 * Fires when the textarea is updated with content from the editor iframe.
23601 * @param {Roo.HtmlEditorCore} this
23602 * @param {String} html
23607 * Fires when the iframe editor is updated with content from the textarea.
23608 * @param {Roo.HtmlEditorCore} this
23609 * @param {String} html
23614 * @event editorevent
23615 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23616 * @param {Roo.HtmlEditorCore} this
23622 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23624 // defaults : white / black...
23625 this.applyBlacklists();
23632 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23636 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23642 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23647 * @cfg {Number} height (in pixels)
23651 * @cfg {Number} width (in pixels)
23656 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23659 stylesheets: false,
23664 // private properties
23665 validationEvent : false,
23667 initialized : false,
23669 sourceEditMode : false,
23670 onFocus : Roo.emptyFn,
23672 hideMode:'offsets',
23676 // blacklist + whitelisted elements..
23683 * Protected method that will not generally be called directly. It
23684 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23685 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23687 getDocMarkup : function(){
23691 // inherit styels from page...??
23692 if (this.stylesheets === false) {
23694 Roo.get(document.head).select('style').each(function(node) {
23695 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23698 Roo.get(document.head).select('link').each(function(node) {
23699 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23702 } else if (!this.stylesheets.length) {
23704 st = '<style type="text/css">' +
23705 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23708 st = '<style type="text/css">' +
23713 st += '<style type="text/css">' +
23714 'IMG { cursor: pointer } ' +
23717 var cls = 'roo-htmleditor-body';
23719 if(this.bodyCls.length){
23720 cls += ' ' + this.bodyCls;
23723 return '<html><head>' + st +
23724 //<style type="text/css">' +
23725 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23727 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23731 onRender : function(ct, position)
23734 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23735 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23738 this.el.dom.style.border = '0 none';
23739 this.el.dom.setAttribute('tabIndex', -1);
23740 this.el.addClass('x-hidden hide');
23744 if(Roo.isIE){ // fix IE 1px bogus margin
23745 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23749 this.frameId = Roo.id();
23753 var iframe = this.owner.wrap.createChild({
23755 cls: 'form-control', // bootstrap..
23757 name: this.frameId,
23758 frameBorder : 'no',
23759 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23764 this.iframe = iframe.dom;
23766 this.assignDocWin();
23768 this.doc.designMode = 'on';
23771 this.doc.write(this.getDocMarkup());
23775 var task = { // must defer to wait for browser to be ready
23777 //console.log("run task?" + this.doc.readyState);
23778 this.assignDocWin();
23779 if(this.doc.body || this.doc.readyState == 'complete'){
23781 this.doc.designMode="on";
23785 Roo.TaskMgr.stop(task);
23786 this.initEditor.defer(10, this);
23793 Roo.TaskMgr.start(task);
23798 onResize : function(w, h)
23800 Roo.log('resize: ' +w + ',' + h );
23801 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23805 if(typeof w == 'number'){
23807 this.iframe.style.width = w + 'px';
23809 if(typeof h == 'number'){
23811 this.iframe.style.height = h + 'px';
23813 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23820 * Toggles the editor between standard and source edit mode.
23821 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23823 toggleSourceEdit : function(sourceEditMode){
23825 this.sourceEditMode = sourceEditMode === true;
23827 if(this.sourceEditMode){
23829 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23832 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23833 //this.iframe.className = '';
23836 //this.setSize(this.owner.wrap.getSize());
23837 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23844 * Protected method that will not generally be called directly. If you need/want
23845 * custom HTML cleanup, this is the method you should override.
23846 * @param {String} html The HTML to be cleaned
23847 * return {String} The cleaned HTML
23849 cleanHtml : function(html){
23850 html = String(html);
23851 if(html.length > 5){
23852 if(Roo.isSafari){ // strip safari nonsense
23853 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23856 if(html == ' '){
23863 * HTML Editor -> Textarea
23864 * Protected method that will not generally be called directly. Syncs the contents
23865 * of the editor iframe with the textarea.
23867 syncValue : function(){
23868 if(this.initialized){
23869 var bd = (this.doc.body || this.doc.documentElement);
23870 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23871 var html = bd.innerHTML;
23873 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23874 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23876 html = '<div style="'+m[0]+'">' + html + '</div>';
23879 html = this.cleanHtml(html);
23880 // fix up the special chars.. normaly like back quotes in word...
23881 // however we do not want to do this with chinese..
23882 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23884 var cc = match.charCodeAt();
23886 // Get the character value, handling surrogate pairs
23887 if (match.length == 2) {
23888 // It's a surrogate pair, calculate the Unicode code point
23889 var high = match.charCodeAt(0) - 0xD800;
23890 var low = match.charCodeAt(1) - 0xDC00;
23891 cc = (high * 0x400) + low + 0x10000;
23893 (cc >= 0x4E00 && cc < 0xA000 ) ||
23894 (cc >= 0x3400 && cc < 0x4E00 ) ||
23895 (cc >= 0xf900 && cc < 0xfb00 )
23900 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23901 return "&#" + cc + ";";
23908 if(this.owner.fireEvent('beforesync', this, html) !== false){
23909 this.el.dom.value = html;
23910 this.owner.fireEvent('sync', this, html);
23916 * Protected method that will not generally be called directly. Pushes the value of the textarea
23917 * into the iframe editor.
23919 pushValue : function(){
23920 if(this.initialized){
23921 var v = this.el.dom.value.trim();
23923 // if(v.length < 1){
23927 if(this.owner.fireEvent('beforepush', this, v) !== false){
23928 var d = (this.doc.body || this.doc.documentElement);
23930 this.cleanUpPaste();
23931 this.el.dom.value = d.innerHTML;
23932 this.owner.fireEvent('push', this, v);
23938 deferFocus : function(){
23939 this.focus.defer(10, this);
23943 focus : function(){
23944 if(this.win && !this.sourceEditMode){
23951 assignDocWin: function()
23953 var iframe = this.iframe;
23956 this.doc = iframe.contentWindow.document;
23957 this.win = iframe.contentWindow;
23959 // if (!Roo.get(this.frameId)) {
23962 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23963 // this.win = Roo.get(this.frameId).dom.contentWindow;
23965 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23969 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23970 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23975 initEditor : function(){
23976 //console.log("INIT EDITOR");
23977 this.assignDocWin();
23981 this.doc.designMode="on";
23983 this.doc.write(this.getDocMarkup());
23986 var dbody = (this.doc.body || this.doc.documentElement);
23987 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23988 // this copies styles from the containing element into thsi one..
23989 // not sure why we need all of this..
23990 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23992 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23993 //ss['background-attachment'] = 'fixed'; // w3c
23994 dbody.bgProperties = 'fixed'; // ie
23995 //Roo.DomHelper.applyStyles(dbody, ss);
23996 Roo.EventManager.on(this.doc, {
23997 //'mousedown': this.onEditorEvent,
23998 'mouseup': this.onEditorEvent,
23999 'dblclick': this.onEditorEvent,
24000 'click': this.onEditorEvent,
24001 'keyup': this.onEditorEvent,
24006 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24008 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24009 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24011 this.initialized = true;
24013 this.owner.fireEvent('initialize', this);
24018 onDestroy : function(){
24024 //for (var i =0; i < this.toolbars.length;i++) {
24025 // // fixme - ask toolbars for heights?
24026 // this.toolbars[i].onDestroy();
24029 //this.wrap.dom.innerHTML = '';
24030 //this.wrap.remove();
24035 onFirstFocus : function(){
24037 this.assignDocWin();
24040 this.activated = true;
24043 if(Roo.isGecko){ // prevent silly gecko errors
24045 var s = this.win.getSelection();
24046 if(!s.focusNode || s.focusNode.nodeType != 3){
24047 var r = s.getRangeAt(0);
24048 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24053 this.execCmd('useCSS', true);
24054 this.execCmd('styleWithCSS', false);
24057 this.owner.fireEvent('activate', this);
24061 adjustFont: function(btn){
24062 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24063 //if(Roo.isSafari){ // safari
24066 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24067 if(Roo.isSafari){ // safari
24068 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24069 v = (v < 10) ? 10 : v;
24070 v = (v > 48) ? 48 : v;
24071 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24076 v = Math.max(1, v+adjust);
24078 this.execCmd('FontSize', v );
24081 onEditorEvent : function(e)
24083 this.owner.fireEvent('editorevent', this, e);
24084 // this.updateToolbar();
24085 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24088 insertTag : function(tg)
24090 // could be a bit smarter... -> wrap the current selected tRoo..
24091 if (tg.toLowerCase() == 'span' ||
24092 tg.toLowerCase() == 'code' ||
24093 tg.toLowerCase() == 'sup' ||
24094 tg.toLowerCase() == 'sub'
24097 range = this.createRange(this.getSelection());
24098 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24099 wrappingNode.appendChild(range.extractContents());
24100 range.insertNode(wrappingNode);
24107 this.execCmd("formatblock", tg);
24111 insertText : function(txt)
24115 var range = this.createRange();
24116 range.deleteContents();
24117 //alert(Sender.getAttribute('label'));
24119 range.insertNode(this.doc.createTextNode(txt));
24125 * Executes a Midas editor command on the editor document and performs necessary focus and
24126 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24127 * @param {String} cmd The Midas command
24128 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24130 relayCmd : function(cmd, value){
24132 this.execCmd(cmd, value);
24133 this.owner.fireEvent('editorevent', this);
24134 //this.updateToolbar();
24135 this.owner.deferFocus();
24139 * Executes a Midas editor command directly on the editor document.
24140 * For visual commands, you should use {@link #relayCmd} instead.
24141 * <b>This should only be called after the editor is initialized.</b>
24142 * @param {String} cmd The Midas command
24143 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24145 execCmd : function(cmd, value){
24146 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24153 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24155 * @param {String} text | dom node..
24157 insertAtCursor : function(text)
24160 if(!this.activated){
24166 var r = this.doc.selection.createRange();
24177 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24181 // from jquery ui (MIT licenced)
24183 var win = this.win;
24185 if (win.getSelection && win.getSelection().getRangeAt) {
24186 range = win.getSelection().getRangeAt(0);
24187 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24188 range.insertNode(node);
24189 } else if (win.document.selection && win.document.selection.createRange) {
24190 // no firefox support
24191 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24192 win.document.selection.createRange().pasteHTML(txt);
24194 // no firefox support
24195 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24196 this.execCmd('InsertHTML', txt);
24205 mozKeyPress : function(e){
24207 var c = e.getCharCode(), cmd;
24210 c = String.fromCharCode(c).toLowerCase();
24224 this.cleanUpPaste.defer(100, this);
24232 e.preventDefault();
24240 fixKeys : function(){ // load time branching for fastest keydown performance
24242 return function(e){
24243 var k = e.getKey(), r;
24246 r = this.doc.selection.createRange();
24249 r.pasteHTML('    ');
24256 r = this.doc.selection.createRange();
24258 var target = r.parentElement();
24259 if(!target || target.tagName.toLowerCase() != 'li'){
24261 r.pasteHTML('<br />');
24267 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24268 this.cleanUpPaste.defer(100, this);
24274 }else if(Roo.isOpera){
24275 return function(e){
24276 var k = e.getKey();
24280 this.execCmd('InsertHTML','    ');
24283 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24284 this.cleanUpPaste.defer(100, this);
24289 }else if(Roo.isSafari){
24290 return function(e){
24291 var k = e.getKey();
24295 this.execCmd('InsertText','\t');
24299 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24300 this.cleanUpPaste.defer(100, this);
24308 getAllAncestors: function()
24310 var p = this.getSelectedNode();
24313 a.push(p); // push blank onto stack..
24314 p = this.getParentElement();
24318 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24322 a.push(this.doc.body);
24326 lastSelNode : false,
24329 getSelection : function()
24331 this.assignDocWin();
24332 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24335 getSelectedNode: function()
24337 // this may only work on Gecko!!!
24339 // should we cache this!!!!
24344 var range = this.createRange(this.getSelection()).cloneRange();
24347 var parent = range.parentElement();
24349 var testRange = range.duplicate();
24350 testRange.moveToElementText(parent);
24351 if (testRange.inRange(range)) {
24354 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24357 parent = parent.parentElement;
24362 // is ancestor a text element.
24363 var ac = range.commonAncestorContainer;
24364 if (ac.nodeType == 3) {
24365 ac = ac.parentNode;
24368 var ar = ac.childNodes;
24371 var other_nodes = [];
24372 var has_other_nodes = false;
24373 for (var i=0;i<ar.length;i++) {
24374 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24377 // fullly contained node.
24379 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24384 // probably selected..
24385 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24386 other_nodes.push(ar[i]);
24390 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24395 has_other_nodes = true;
24397 if (!nodes.length && other_nodes.length) {
24398 nodes= other_nodes;
24400 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24406 createRange: function(sel)
24408 // this has strange effects when using with
24409 // top toolbar - not sure if it's a great idea.
24410 //this.editor.contentWindow.focus();
24411 if (typeof sel != "undefined") {
24413 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24415 return this.doc.createRange();
24418 return this.doc.createRange();
24421 getParentElement: function()
24424 this.assignDocWin();
24425 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24427 var range = this.createRange(sel);
24430 var p = range.commonAncestorContainer;
24431 while (p.nodeType == 3) { // text node
24442 * Range intersection.. the hard stuff...
24446 * [ -- selected range --- ]
24450 * if end is before start or hits it. fail.
24451 * if start is after end or hits it fail.
24453 * if either hits (but other is outside. - then it's not
24459 // @see http://www.thismuchiknow.co.uk/?p=64.
24460 rangeIntersectsNode : function(range, node)
24462 var nodeRange = node.ownerDocument.createRange();
24464 nodeRange.selectNode(node);
24466 nodeRange.selectNodeContents(node);
24469 var rangeStartRange = range.cloneRange();
24470 rangeStartRange.collapse(true);
24472 var rangeEndRange = range.cloneRange();
24473 rangeEndRange.collapse(false);
24475 var nodeStartRange = nodeRange.cloneRange();
24476 nodeStartRange.collapse(true);
24478 var nodeEndRange = nodeRange.cloneRange();
24479 nodeEndRange.collapse(false);
24481 return rangeStartRange.compareBoundaryPoints(
24482 Range.START_TO_START, nodeEndRange) == -1 &&
24483 rangeEndRange.compareBoundaryPoints(
24484 Range.START_TO_START, nodeStartRange) == 1;
24488 rangeCompareNode : function(range, node)
24490 var nodeRange = node.ownerDocument.createRange();
24492 nodeRange.selectNode(node);
24494 nodeRange.selectNodeContents(node);
24498 range.collapse(true);
24500 nodeRange.collapse(true);
24502 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24503 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24505 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24507 var nodeIsBefore = ss == 1;
24508 var nodeIsAfter = ee == -1;
24510 if (nodeIsBefore && nodeIsAfter) {
24513 if (!nodeIsBefore && nodeIsAfter) {
24514 return 1; //right trailed.
24517 if (nodeIsBefore && !nodeIsAfter) {
24518 return 2; // left trailed.
24524 // private? - in a new class?
24525 cleanUpPaste : function()
24527 // cleans up the whole document..
24528 Roo.log('cleanuppaste');
24530 this.cleanUpChildren(this.doc.body);
24531 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24532 if (clean != this.doc.body.innerHTML) {
24533 this.doc.body.innerHTML = clean;
24538 cleanWordChars : function(input) {// change the chars to hex code
24539 var he = Roo.HtmlEditorCore;
24541 var output = input;
24542 Roo.each(he.swapCodes, function(sw) {
24543 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24545 output = output.replace(swapper, sw[1]);
24552 cleanUpChildren : function (n)
24554 if (!n.childNodes.length) {
24557 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24558 this.cleanUpChild(n.childNodes[i]);
24565 cleanUpChild : function (node)
24568 //console.log(node);
24569 if (node.nodeName == "#text") {
24570 // clean up silly Windows -- stuff?
24573 if (node.nodeName == "#comment") {
24574 node.parentNode.removeChild(node);
24575 // clean up silly Windows -- stuff?
24578 var lcname = node.tagName.toLowerCase();
24579 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24580 // whitelist of tags..
24582 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24584 node.parentNode.removeChild(node);
24589 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24591 // spans with no attributes - just remove them..
24592 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24593 remove_keep_children = true;
24596 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24597 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24599 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24600 // remove_keep_children = true;
24603 if (remove_keep_children) {
24604 this.cleanUpChildren(node);
24605 // inserts everything just before this node...
24606 while (node.childNodes.length) {
24607 var cn = node.childNodes[0];
24608 node.removeChild(cn);
24609 node.parentNode.insertBefore(cn, node);
24611 node.parentNode.removeChild(node);
24615 if (!node.attributes || !node.attributes.length) {
24620 this.cleanUpChildren(node);
24624 function cleanAttr(n,v)
24627 if (v.match(/^\./) || v.match(/^\//)) {
24630 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24633 if (v.match(/^#/)) {
24636 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24637 node.removeAttribute(n);
24641 var cwhite = this.cwhite;
24642 var cblack = this.cblack;
24644 function cleanStyle(n,v)
24646 if (v.match(/expression/)) { //XSS?? should we even bother..
24647 node.removeAttribute(n);
24651 var parts = v.split(/;/);
24654 Roo.each(parts, function(p) {
24655 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24659 var l = p.split(':').shift().replace(/\s+/g,'');
24660 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24662 if ( cwhite.length && cblack.indexOf(l) > -1) {
24663 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24664 //node.removeAttribute(n);
24668 // only allow 'c whitelisted system attributes'
24669 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24670 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24671 //node.removeAttribute(n);
24681 if (clean.length) {
24682 node.setAttribute(n, clean.join(';'));
24684 node.removeAttribute(n);
24690 for (var i = node.attributes.length-1; i > -1 ; i--) {
24691 var a = node.attributes[i];
24694 if (a.name.toLowerCase().substr(0,2)=='on') {
24695 node.removeAttribute(a.name);
24698 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24699 node.removeAttribute(a.name);
24702 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24703 cleanAttr(a.name,a.value); // fixme..
24706 if (a.name == 'style') {
24707 cleanStyle(a.name,a.value);
24710 /// clean up MS crap..
24711 // tecnically this should be a list of valid class'es..
24714 if (a.name == 'class') {
24715 if (a.value.match(/^Mso/)) {
24716 node.removeAttribute('class');
24719 if (a.value.match(/^body$/)) {
24720 node.removeAttribute('class');
24731 this.cleanUpChildren(node);
24737 * Clean up MS wordisms...
24739 cleanWord : function(node)
24742 this.cleanWord(this.doc.body);
24747 node.nodeName == 'SPAN' &&
24748 !node.hasAttributes() &&
24749 node.childNodes.length == 1 &&
24750 node.firstChild.nodeName == "#text"
24752 var textNode = node.firstChild;
24753 node.removeChild(textNode);
24754 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24755 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24757 node.parentNode.insertBefore(textNode, node);
24758 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24759 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24761 node.parentNode.removeChild(node);
24764 if (node.nodeName == "#text") {
24765 // clean up silly Windows -- stuff?
24768 if (node.nodeName == "#comment") {
24769 node.parentNode.removeChild(node);
24770 // clean up silly Windows -- stuff?
24774 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24775 node.parentNode.removeChild(node);
24778 //Roo.log(node.tagName);
24779 // remove - but keep children..
24780 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24781 //Roo.log('-- removed');
24782 while (node.childNodes.length) {
24783 var cn = node.childNodes[0];
24784 node.removeChild(cn);
24785 node.parentNode.insertBefore(cn, node);
24786 // move node to parent - and clean it..
24787 this.cleanWord(cn);
24789 node.parentNode.removeChild(node);
24790 /// no need to iterate chidlren = it's got none..
24791 //this.iterateChildren(node, this.cleanWord);
24795 if (node.className.length) {
24797 var cn = node.className.split(/\W+/);
24799 Roo.each(cn, function(cls) {
24800 if (cls.match(/Mso[a-zA-Z]+/)) {
24805 node.className = cna.length ? cna.join(' ') : '';
24807 node.removeAttribute("class");
24811 if (node.hasAttribute("lang")) {
24812 node.removeAttribute("lang");
24815 if (node.hasAttribute("style")) {
24817 var styles = node.getAttribute("style").split(";");
24819 Roo.each(styles, function(s) {
24820 if (!s.match(/:/)) {
24823 var kv = s.split(":");
24824 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24827 // what ever is left... we allow.
24830 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24831 if (!nstyle.length) {
24832 node.removeAttribute('style');
24835 this.iterateChildren(node, this.cleanWord);
24841 * iterateChildren of a Node, calling fn each time, using this as the scole..
24842 * @param {DomNode} node node to iterate children of.
24843 * @param {Function} fn method of this class to call on each item.
24845 iterateChildren : function(node, fn)
24847 if (!node.childNodes.length) {
24850 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24851 fn.call(this, node.childNodes[i])
24857 * cleanTableWidths.
24859 * Quite often pasting from word etc.. results in tables with column and widths.
24860 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24863 cleanTableWidths : function(node)
24868 this.cleanTableWidths(this.doc.body);
24873 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24876 Roo.log(node.tagName);
24877 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24878 this.iterateChildren(node, this.cleanTableWidths);
24881 if (node.hasAttribute('width')) {
24882 node.removeAttribute('width');
24886 if (node.hasAttribute("style")) {
24889 var styles = node.getAttribute("style").split(";");
24891 Roo.each(styles, function(s) {
24892 if (!s.match(/:/)) {
24895 var kv = s.split(":");
24896 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24899 // what ever is left... we allow.
24902 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24903 if (!nstyle.length) {
24904 node.removeAttribute('style');
24908 this.iterateChildren(node, this.cleanTableWidths);
24916 domToHTML : function(currentElement, depth, nopadtext) {
24918 depth = depth || 0;
24919 nopadtext = nopadtext || false;
24921 if (!currentElement) {
24922 return this.domToHTML(this.doc.body);
24925 //Roo.log(currentElement);
24927 var allText = false;
24928 var nodeName = currentElement.nodeName;
24929 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24931 if (nodeName == '#text') {
24933 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24938 if (nodeName != 'BODY') {
24941 // Prints the node tagName, such as <A>, <IMG>, etc
24944 for(i = 0; i < currentElement.attributes.length;i++) {
24946 var aname = currentElement.attributes.item(i).name;
24947 if (!currentElement.attributes.item(i).value.length) {
24950 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24953 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24962 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24965 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24970 // Traverse the tree
24972 var currentElementChild = currentElement.childNodes.item(i);
24973 var allText = true;
24974 var innerHTML = '';
24976 while (currentElementChild) {
24977 // Formatting code (indent the tree so it looks nice on the screen)
24978 var nopad = nopadtext;
24979 if (lastnode == 'SPAN') {
24983 if (currentElementChild.nodeName == '#text') {
24984 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24985 toadd = nopadtext ? toadd : toadd.trim();
24986 if (!nopad && toadd.length > 80) {
24987 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24989 innerHTML += toadd;
24992 currentElementChild = currentElement.childNodes.item(i);
24998 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25000 // Recursively traverse the tree structure of the child node
25001 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25002 lastnode = currentElementChild.nodeName;
25004 currentElementChild=currentElement.childNodes.item(i);
25010 // The remaining code is mostly for formatting the tree
25011 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25016 ret+= "</"+tagName+">";
25022 applyBlacklists : function()
25024 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25025 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25029 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25030 if (b.indexOf(tag) > -1) {
25033 this.white.push(tag);
25037 Roo.each(w, function(tag) {
25038 if (b.indexOf(tag) > -1) {
25041 if (this.white.indexOf(tag) > -1) {
25044 this.white.push(tag);
25049 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25050 if (w.indexOf(tag) > -1) {
25053 this.black.push(tag);
25057 Roo.each(b, function(tag) {
25058 if (w.indexOf(tag) > -1) {
25061 if (this.black.indexOf(tag) > -1) {
25064 this.black.push(tag);
25069 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25070 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25074 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25075 if (b.indexOf(tag) > -1) {
25078 this.cwhite.push(tag);
25082 Roo.each(w, function(tag) {
25083 if (b.indexOf(tag) > -1) {
25086 if (this.cwhite.indexOf(tag) > -1) {
25089 this.cwhite.push(tag);
25094 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25095 if (w.indexOf(tag) > -1) {
25098 this.cblack.push(tag);
25102 Roo.each(b, function(tag) {
25103 if (w.indexOf(tag) > -1) {
25106 if (this.cblack.indexOf(tag) > -1) {
25109 this.cblack.push(tag);
25114 setStylesheets : function(stylesheets)
25116 if(typeof(stylesheets) == 'string'){
25117 Roo.get(this.iframe.contentDocument.head).createChild({
25119 rel : 'stylesheet',
25128 Roo.each(stylesheets, function(s) {
25133 Roo.get(_this.iframe.contentDocument.head).createChild({
25135 rel : 'stylesheet',
25144 removeStylesheets : function()
25148 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25153 setStyle : function(style)
25155 Roo.get(this.iframe.contentDocument.head).createChild({
25164 // hide stuff that is not compatible
25178 * @event specialkey
25182 * @cfg {String} fieldClass @hide
25185 * @cfg {String} focusClass @hide
25188 * @cfg {String} autoCreate @hide
25191 * @cfg {String} inputType @hide
25194 * @cfg {String} invalidClass @hide
25197 * @cfg {String} invalidText @hide
25200 * @cfg {String} msgFx @hide
25203 * @cfg {String} validateOnBlur @hide
25207 Roo.HtmlEditorCore.white = [
25208 'area', 'br', 'img', 'input', 'hr', 'wbr',
25210 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25211 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25212 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25213 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25214 'table', 'ul', 'xmp',
25216 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25219 'dir', 'menu', 'ol', 'ul', 'dl',
25225 Roo.HtmlEditorCore.black = [
25226 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25228 'base', 'basefont', 'bgsound', 'blink', 'body',
25229 'frame', 'frameset', 'head', 'html', 'ilayer',
25230 'iframe', 'layer', 'link', 'meta', 'object',
25231 'script', 'style' ,'title', 'xml' // clean later..
25233 Roo.HtmlEditorCore.clean = [
25234 'script', 'style', 'title', 'xml'
25236 Roo.HtmlEditorCore.remove = [
25241 Roo.HtmlEditorCore.ablack = [
25245 Roo.HtmlEditorCore.aclean = [
25246 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25250 Roo.HtmlEditorCore.pwhite= [
25251 'http', 'https', 'mailto'
25254 // white listed style attributes.
25255 Roo.HtmlEditorCore.cwhite= [
25256 // 'text-align', /// default is to allow most things..
25262 // black listed style attributes.
25263 Roo.HtmlEditorCore.cblack= [
25264 // 'font-size' -- this can be set by the project
25268 Roo.HtmlEditorCore.swapCodes =[
25287 * @class Roo.bootstrap.HtmlEditor
25288 * @extends Roo.bootstrap.TextArea
25289 * Bootstrap HtmlEditor class
25292 * Create a new HtmlEditor
25293 * @param {Object} config The config object
25296 Roo.bootstrap.HtmlEditor = function(config){
25297 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25298 if (!this.toolbars) {
25299 this.toolbars = [];
25302 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25305 * @event initialize
25306 * Fires when the editor is fully initialized (including the iframe)
25307 * @param {HtmlEditor} this
25312 * Fires when the editor is first receives the focus. Any insertion must wait
25313 * until after this event.
25314 * @param {HtmlEditor} this
25318 * @event beforesync
25319 * Fires before the textarea is updated with content from the editor iframe. Return false
25320 * to cancel the sync.
25321 * @param {HtmlEditor} this
25322 * @param {String} html
25326 * @event beforepush
25327 * Fires before the iframe editor is updated with content from the textarea. Return false
25328 * to cancel the push.
25329 * @param {HtmlEditor} this
25330 * @param {String} html
25335 * Fires when the textarea is updated with content from the editor iframe.
25336 * @param {HtmlEditor} this
25337 * @param {String} html
25342 * Fires when the iframe editor is updated with content from the textarea.
25343 * @param {HtmlEditor} this
25344 * @param {String} html
25348 * @event editmodechange
25349 * Fires when the editor switches edit modes
25350 * @param {HtmlEditor} this
25351 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25353 editmodechange: true,
25355 * @event editorevent
25356 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25357 * @param {HtmlEditor} this
25361 * @event firstfocus
25362 * Fires when on first focus - needed by toolbars..
25363 * @param {HtmlEditor} this
25368 * Auto save the htmlEditor value as a file into Events
25369 * @param {HtmlEditor} this
25373 * @event savedpreview
25374 * preview the saved version of htmlEditor
25375 * @param {HtmlEditor} this
25382 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25386 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25391 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25396 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25401 * @cfg {Number} height (in pixels)
25405 * @cfg {Number} width (in pixels)
25410 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25413 stylesheets: false,
25418 // private properties
25419 validationEvent : false,
25421 initialized : false,
25424 onFocus : Roo.emptyFn,
25426 hideMode:'offsets',
25428 tbContainer : false,
25432 toolbarContainer :function() {
25433 return this.wrap.select('.x-html-editor-tb',true).first();
25437 * Protected method that will not generally be called directly. It
25438 * is called when the editor creates its toolbar. Override this method if you need to
25439 * add custom toolbar buttons.
25440 * @param {HtmlEditor} editor
25442 createToolbar : function(){
25443 Roo.log('renewing');
25444 Roo.log("create toolbars");
25446 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25447 this.toolbars[0].render(this.toolbarContainer());
25451 // if (!editor.toolbars || !editor.toolbars.length) {
25452 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25455 // for (var i =0 ; i < editor.toolbars.length;i++) {
25456 // editor.toolbars[i] = Roo.factory(
25457 // typeof(editor.toolbars[i]) == 'string' ?
25458 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25459 // Roo.bootstrap.HtmlEditor);
25460 // editor.toolbars[i].init(editor);
25466 onRender : function(ct, position)
25468 // Roo.log("Call onRender: " + this.xtype);
25470 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25472 this.wrap = this.inputEl().wrap({
25473 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25476 this.editorcore.onRender(ct, position);
25478 if (this.resizable) {
25479 this.resizeEl = new Roo.Resizable(this.wrap, {
25483 minHeight : this.height,
25484 height: this.height,
25485 handles : this.resizable,
25488 resize : function(r, w, h) {
25489 _t.onResize(w,h); // -something
25495 this.createToolbar(this);
25498 if(!this.width && this.resizable){
25499 this.setSize(this.wrap.getSize());
25501 if (this.resizeEl) {
25502 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25503 // should trigger onReize..
25509 onResize : function(w, h)
25511 Roo.log('resize: ' +w + ',' + h );
25512 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25516 if(this.inputEl() ){
25517 if(typeof w == 'number'){
25518 var aw = w - this.wrap.getFrameWidth('lr');
25519 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25522 if(typeof h == 'number'){
25523 var tbh = -11; // fixme it needs to tool bar size!
25524 for (var i =0; i < this.toolbars.length;i++) {
25525 // fixme - ask toolbars for heights?
25526 tbh += this.toolbars[i].el.getHeight();
25527 //if (this.toolbars[i].footer) {
25528 // tbh += this.toolbars[i].footer.el.getHeight();
25536 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25537 ah -= 5; // knock a few pixes off for look..
25538 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25542 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25543 this.editorcore.onResize(ew,eh);
25548 * Toggles the editor between standard and source edit mode.
25549 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25551 toggleSourceEdit : function(sourceEditMode)
25553 this.editorcore.toggleSourceEdit(sourceEditMode);
25555 if(this.editorcore.sourceEditMode){
25556 Roo.log('editor - showing textarea');
25559 // Roo.log(this.syncValue());
25561 this.inputEl().removeClass(['hide', 'x-hidden']);
25562 this.inputEl().dom.removeAttribute('tabIndex');
25563 this.inputEl().focus();
25565 Roo.log('editor - hiding textarea');
25567 // Roo.log(this.pushValue());
25570 this.inputEl().addClass(['hide', 'x-hidden']);
25571 this.inputEl().dom.setAttribute('tabIndex', -1);
25572 //this.deferFocus();
25575 if(this.resizable){
25576 this.setSize(this.wrap.getSize());
25579 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25582 // private (for BoxComponent)
25583 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25585 // private (for BoxComponent)
25586 getResizeEl : function(){
25590 // private (for BoxComponent)
25591 getPositionEl : function(){
25596 initEvents : function(){
25597 this.originalValue = this.getValue();
25601 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25604 // markInvalid : Roo.emptyFn,
25606 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25609 // clearInvalid : Roo.emptyFn,
25611 setValue : function(v){
25612 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25613 this.editorcore.pushValue();
25618 deferFocus : function(){
25619 this.focus.defer(10, this);
25623 focus : function(){
25624 this.editorcore.focus();
25630 onDestroy : function(){
25636 for (var i =0; i < this.toolbars.length;i++) {
25637 // fixme - ask toolbars for heights?
25638 this.toolbars[i].onDestroy();
25641 this.wrap.dom.innerHTML = '';
25642 this.wrap.remove();
25647 onFirstFocus : function(){
25648 //Roo.log("onFirstFocus");
25649 this.editorcore.onFirstFocus();
25650 for (var i =0; i < this.toolbars.length;i++) {
25651 this.toolbars[i].onFirstFocus();
25657 syncValue : function()
25659 this.editorcore.syncValue();
25662 pushValue : function()
25664 this.editorcore.pushValue();
25668 // hide stuff that is not compatible
25682 * @event specialkey
25686 * @cfg {String} fieldClass @hide
25689 * @cfg {String} focusClass @hide
25692 * @cfg {String} autoCreate @hide
25695 * @cfg {String} inputType @hide
25699 * @cfg {String} invalidText @hide
25702 * @cfg {String} msgFx @hide
25705 * @cfg {String} validateOnBlur @hide
25714 Roo.namespace('Roo.bootstrap.htmleditor');
25716 * @class Roo.bootstrap.HtmlEditorToolbar1
25722 new Roo.bootstrap.HtmlEditor({
25725 new Roo.bootstrap.HtmlEditorToolbar1({
25726 disable : { fonts: 1 , format: 1, ..., ... , ...],
25732 * @cfg {Object} disable List of elements to disable..
25733 * @cfg {Array} btns List of additional buttons.
25737 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25740 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25743 Roo.apply(this, config);
25745 // default disabled, based on 'good practice'..
25746 this.disable = this.disable || {};
25747 Roo.applyIf(this.disable, {
25750 specialElements : true
25752 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25754 this.editor = config.editor;
25755 this.editorcore = config.editor.editorcore;
25757 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25759 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25760 // dont call parent... till later.
25762 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25767 editorcore : false,
25772 "h1","h2","h3","h4","h5","h6",
25774 "abbr", "acronym", "address", "cite", "samp", "var",
25778 onRender : function(ct, position)
25780 // Roo.log("Call onRender: " + this.xtype);
25782 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25784 this.el.dom.style.marginBottom = '0';
25786 var editorcore = this.editorcore;
25787 var editor= this.editor;
25790 var btn = function(id,cmd , toggle, handler, html){
25792 var event = toggle ? 'toggle' : 'click';
25797 xns: Roo.bootstrap,
25801 enableToggle:toggle !== false,
25803 pressed : toggle ? false : null,
25806 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25807 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25813 // var cb_box = function...
25818 xns: Roo.bootstrap,
25823 xns: Roo.bootstrap,
25827 Roo.each(this.formats, function(f) {
25828 style.menu.items.push({
25830 xns: Roo.bootstrap,
25831 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25836 editorcore.insertTag(this.tagname);
25843 children.push(style);
25845 btn('bold',false,true);
25846 btn('italic',false,true);
25847 btn('align-left', 'justifyleft',true);
25848 btn('align-center', 'justifycenter',true);
25849 btn('align-right' , 'justifyright',true);
25850 btn('link', false, false, function(btn) {
25851 //Roo.log("create link?");
25852 var url = prompt(this.createLinkText, this.defaultLinkValue);
25853 if(url && url != 'http:/'+'/'){
25854 this.editorcore.relayCmd('createlink', url);
25857 btn('list','insertunorderedlist',true);
25858 btn('pencil', false,true, function(btn){
25860 this.toggleSourceEdit(btn.pressed);
25863 if (this.editor.btns.length > 0) {
25864 for (var i = 0; i<this.editor.btns.length; i++) {
25865 children.push(this.editor.btns[i]);
25873 xns: Roo.bootstrap,
25878 xns: Roo.bootstrap,
25883 cog.menu.items.push({
25885 xns: Roo.bootstrap,
25886 html : Clean styles,
25891 editorcore.insertTag(this.tagname);
25900 this.xtype = 'NavSimplebar';
25902 for(var i=0;i< children.length;i++) {
25904 this.buttons.add(this.addxtypeChild(children[i]));
25908 editor.on('editorevent', this.updateToolbar, this);
25910 onBtnClick : function(id)
25912 this.editorcore.relayCmd(id);
25913 this.editorcore.focus();
25917 * Protected method that will not generally be called directly. It triggers
25918 * a toolbar update by reading the markup state of the current selection in the editor.
25920 updateToolbar: function(){
25922 if(!this.editorcore.activated){
25923 this.editor.onFirstFocus(); // is this neeed?
25927 var btns = this.buttons;
25928 var doc = this.editorcore.doc;
25929 btns.get('bold').setActive(doc.queryCommandState('bold'));
25930 btns.get('italic').setActive(doc.queryCommandState('italic'));
25931 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25933 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25934 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25935 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25937 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25938 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25941 var ans = this.editorcore.getAllAncestors();
25942 if (this.formatCombo) {
25945 var store = this.formatCombo.store;
25946 this.formatCombo.setValue("");
25947 for (var i =0; i < ans.length;i++) {
25948 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25950 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25958 // hides menus... - so this cant be on a menu...
25959 Roo.bootstrap.MenuMgr.hideAll();
25961 Roo.bootstrap.MenuMgr.hideAll();
25962 //this.editorsyncValue();
25964 onFirstFocus: function() {
25965 this.buttons.each(function(item){
25969 toggleSourceEdit : function(sourceEditMode){
25972 if(sourceEditMode){
25973 Roo.log("disabling buttons");
25974 this.buttons.each( function(item){
25975 if(item.cmd != 'pencil'){
25981 Roo.log("enabling buttons");
25982 if(this.editorcore.initialized){
25983 this.buttons.each( function(item){
25989 Roo.log("calling toggole on editor");
25990 // tell the editor that it's been pressed..
25991 this.editor.toggleSourceEdit(sourceEditMode);
26001 * @class Roo.bootstrap.Table.AbstractSelectionModel
26002 * @extends Roo.util.Observable
26003 * Abstract base class for grid SelectionModels. It provides the interface that should be
26004 * implemented by descendant classes. This class should not be directly instantiated.
26007 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26008 this.locked = false;
26009 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26013 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26014 /** @ignore Called by the grid automatically. Do not call directly. */
26015 init : function(grid){
26021 * Locks the selections.
26024 this.locked = true;
26028 * Unlocks the selections.
26030 unlock : function(){
26031 this.locked = false;
26035 * Returns true if the selections are locked.
26036 * @return {Boolean}
26038 isLocked : function(){
26039 return this.locked;
26043 initEvents : function ()
26049 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26050 * @class Roo.bootstrap.Table.RowSelectionModel
26051 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26052 * It supports multiple selections and keyboard selection/navigation.
26054 * @param {Object} config
26057 Roo.bootstrap.Table.RowSelectionModel = function(config){
26058 Roo.apply(this, config);
26059 this.selections = new Roo.util.MixedCollection(false, function(o){
26064 this.lastActive = false;
26068 * @event selectionchange
26069 * Fires when the selection changes
26070 * @param {SelectionModel} this
26072 "selectionchange" : true,
26074 * @event afterselectionchange
26075 * Fires after the selection changes (eg. by key press or clicking)
26076 * @param {SelectionModel} this
26078 "afterselectionchange" : true,
26080 * @event beforerowselect
26081 * Fires when a row is selected being selected, return false to cancel.
26082 * @param {SelectionModel} this
26083 * @param {Number} rowIndex The selected index
26084 * @param {Boolean} keepExisting False if other selections will be cleared
26086 "beforerowselect" : true,
26089 * Fires when a row is selected.
26090 * @param {SelectionModel} this
26091 * @param {Number} rowIndex The selected index
26092 * @param {Roo.data.Record} r The record
26094 "rowselect" : true,
26096 * @event rowdeselect
26097 * Fires when a row is deselected.
26098 * @param {SelectionModel} this
26099 * @param {Number} rowIndex The selected index
26101 "rowdeselect" : true
26103 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26104 this.locked = false;
26107 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26109 * @cfg {Boolean} singleSelect
26110 * True to allow selection of only one row at a time (defaults to false)
26112 singleSelect : false,
26115 initEvents : function()
26118 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26119 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26120 //}else{ // allow click to work like normal
26121 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26123 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26124 this.grid.on("rowclick", this.handleMouseDown, this);
26126 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26127 "up" : function(e){
26129 this.selectPrevious(e.shiftKey);
26130 }else if(this.last !== false && this.lastActive !== false){
26131 var last = this.last;
26132 this.selectRange(this.last, this.lastActive-1);
26133 this.grid.getView().focusRow(this.lastActive);
26134 if(last !== false){
26138 this.selectFirstRow();
26140 this.fireEvent("afterselectionchange", this);
26142 "down" : function(e){
26144 this.selectNext(e.shiftKey);
26145 }else if(this.last !== false && this.lastActive !== false){
26146 var last = this.last;
26147 this.selectRange(this.last, this.lastActive+1);
26148 this.grid.getView().focusRow(this.lastActive);
26149 if(last !== false){
26153 this.selectFirstRow();
26155 this.fireEvent("afterselectionchange", this);
26159 this.grid.store.on('load', function(){
26160 this.selections.clear();
26163 var view = this.grid.view;
26164 view.on("refresh", this.onRefresh, this);
26165 view.on("rowupdated", this.onRowUpdated, this);
26166 view.on("rowremoved", this.onRemove, this);
26171 onRefresh : function()
26173 var ds = this.grid.store, i, v = this.grid.view;
26174 var s = this.selections;
26175 s.each(function(r){
26176 if((i = ds.indexOfId(r.id)) != -1){
26185 onRemove : function(v, index, r){
26186 this.selections.remove(r);
26190 onRowUpdated : function(v, index, r){
26191 if(this.isSelected(r)){
26192 v.onRowSelect(index);
26198 * @param {Array} records The records to select
26199 * @param {Boolean} keepExisting (optional) True to keep existing selections
26201 selectRecords : function(records, keepExisting)
26204 this.clearSelections();
26206 var ds = this.grid.store;
26207 for(var i = 0, len = records.length; i < len; i++){
26208 this.selectRow(ds.indexOf(records[i]), true);
26213 * Gets the number of selected rows.
26216 getCount : function(){
26217 return this.selections.length;
26221 * Selects the first row in the grid.
26223 selectFirstRow : function(){
26228 * Select the last row.
26229 * @param {Boolean} keepExisting (optional) True to keep existing selections
26231 selectLastRow : function(keepExisting){
26232 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26233 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26237 * Selects the row immediately following the last selected row.
26238 * @param {Boolean} keepExisting (optional) True to keep existing selections
26240 selectNext : function(keepExisting)
26242 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26243 this.selectRow(this.last+1, keepExisting);
26244 this.grid.getView().focusRow(this.last);
26249 * Selects the row that precedes the last selected row.
26250 * @param {Boolean} keepExisting (optional) True to keep existing selections
26252 selectPrevious : function(keepExisting){
26254 this.selectRow(this.last-1, keepExisting);
26255 this.grid.getView().focusRow(this.last);
26260 * Returns the selected records
26261 * @return {Array} Array of selected records
26263 getSelections : function(){
26264 return [].concat(this.selections.items);
26268 * Returns the first selected record.
26271 getSelected : function(){
26272 return this.selections.itemAt(0);
26277 * Clears all selections.
26279 clearSelections : function(fast)
26285 var ds = this.grid.store;
26286 var s = this.selections;
26287 s.each(function(r){
26288 this.deselectRow(ds.indexOfId(r.id));
26292 this.selections.clear();
26299 * Selects all rows.
26301 selectAll : function(){
26305 this.selections.clear();
26306 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26307 this.selectRow(i, true);
26312 * Returns True if there is a selection.
26313 * @return {Boolean}
26315 hasSelection : function(){
26316 return this.selections.length > 0;
26320 * Returns True if the specified row is selected.
26321 * @param {Number/Record} record The record or index of the record to check
26322 * @return {Boolean}
26324 isSelected : function(index){
26325 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26326 return (r && this.selections.key(r.id) ? true : false);
26330 * Returns True if the specified record id is selected.
26331 * @param {String} id The id of record to check
26332 * @return {Boolean}
26334 isIdSelected : function(id){
26335 return (this.selections.key(id) ? true : false);
26340 handleMouseDBClick : function(e, t){
26344 handleMouseDown : function(e, t)
26346 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26347 if(this.isLocked() || rowIndex < 0 ){
26350 if(e.shiftKey && this.last !== false){
26351 var last = this.last;
26352 this.selectRange(last, rowIndex, e.ctrlKey);
26353 this.last = last; // reset the last
26357 var isSelected = this.isSelected(rowIndex);
26358 //Roo.log("select row:" + rowIndex);
26360 this.deselectRow(rowIndex);
26362 this.selectRow(rowIndex, true);
26366 if(e.button !== 0 && isSelected){
26367 alert('rowIndex 2: ' + rowIndex);
26368 view.focusRow(rowIndex);
26369 }else if(e.ctrlKey && isSelected){
26370 this.deselectRow(rowIndex);
26371 }else if(!isSelected){
26372 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26373 view.focusRow(rowIndex);
26377 this.fireEvent("afterselectionchange", this);
26380 handleDragableRowClick : function(grid, rowIndex, e)
26382 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26383 this.selectRow(rowIndex, false);
26384 grid.view.focusRow(rowIndex);
26385 this.fireEvent("afterselectionchange", this);
26390 * Selects multiple rows.
26391 * @param {Array} rows Array of the indexes of the row to select
26392 * @param {Boolean} keepExisting (optional) True to keep existing selections
26394 selectRows : function(rows, keepExisting){
26396 this.clearSelections();
26398 for(var i = 0, len = rows.length; i < len; i++){
26399 this.selectRow(rows[i], true);
26404 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26405 * @param {Number} startRow The index of the first row in the range
26406 * @param {Number} endRow The index of the last row in the range
26407 * @param {Boolean} keepExisting (optional) True to retain existing selections
26409 selectRange : function(startRow, endRow, keepExisting){
26414 this.clearSelections();
26416 if(startRow <= endRow){
26417 for(var i = startRow; i <= endRow; i++){
26418 this.selectRow(i, true);
26421 for(var i = startRow; i >= endRow; i--){
26422 this.selectRow(i, true);
26428 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26429 * @param {Number} startRow The index of the first row in the range
26430 * @param {Number} endRow The index of the last row in the range
26432 deselectRange : function(startRow, endRow, preventViewNotify){
26436 for(var i = startRow; i <= endRow; i++){
26437 this.deselectRow(i, preventViewNotify);
26443 * @param {Number} row The index of the row to select
26444 * @param {Boolean} keepExisting (optional) True to keep existing selections
26446 selectRow : function(index, keepExisting, preventViewNotify)
26448 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26451 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26452 if(!keepExisting || this.singleSelect){
26453 this.clearSelections();
26456 var r = this.grid.store.getAt(index);
26457 //console.log('selectRow - record id :' + r.id);
26459 this.selections.add(r);
26460 this.last = this.lastActive = index;
26461 if(!preventViewNotify){
26462 var proxy = new Roo.Element(
26463 this.grid.getRowDom(index)
26465 proxy.addClass('bg-info info');
26467 this.fireEvent("rowselect", this, index, r);
26468 this.fireEvent("selectionchange", this);
26474 * @param {Number} row The index of the row to deselect
26476 deselectRow : function(index, preventViewNotify)
26481 if(this.last == index){
26484 if(this.lastActive == index){
26485 this.lastActive = false;
26488 var r = this.grid.store.getAt(index);
26493 this.selections.remove(r);
26494 //.console.log('deselectRow - record id :' + r.id);
26495 if(!preventViewNotify){
26497 var proxy = new Roo.Element(
26498 this.grid.getRowDom(index)
26500 proxy.removeClass('bg-info info');
26502 this.fireEvent("rowdeselect", this, index);
26503 this.fireEvent("selectionchange", this);
26507 restoreLast : function(){
26509 this.last = this._last;
26514 acceptsNav : function(row, col, cm){
26515 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26519 onEditorKey : function(field, e){
26520 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26525 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26527 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26529 }else if(k == e.ENTER && !e.ctrlKey){
26533 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26535 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26537 }else if(k == e.ESC){
26541 g.startEditing(newCell[0], newCell[1]);
26547 * Ext JS Library 1.1.1
26548 * Copyright(c) 2006-2007, Ext JS, LLC.
26550 * Originally Released Under LGPL - original licence link has changed is not relivant.
26553 * <script type="text/javascript">
26557 * @class Roo.bootstrap.PagingToolbar
26558 * @extends Roo.bootstrap.NavSimplebar
26559 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26561 * Create a new PagingToolbar
26562 * @param {Object} config The config object
26563 * @param {Roo.data.Store} store
26565 Roo.bootstrap.PagingToolbar = function(config)
26567 // old args format still supported... - xtype is prefered..
26568 // created from xtype...
26570 this.ds = config.dataSource;
26572 if (config.store && !this.ds) {
26573 this.store= Roo.factory(config.store, Roo.data);
26574 this.ds = this.store;
26575 this.ds.xmodule = this.xmodule || false;
26578 this.toolbarItems = [];
26579 if (config.items) {
26580 this.toolbarItems = config.items;
26583 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26588 this.bind(this.ds);
26591 if (Roo.bootstrap.version == 4) {
26592 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26594 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26599 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26601 * @cfg {Roo.data.Store} dataSource
26602 * The underlying data store providing the paged data
26605 * @cfg {String/HTMLElement/Element} container
26606 * container The id or element that will contain the toolbar
26609 * @cfg {Boolean} displayInfo
26610 * True to display the displayMsg (defaults to false)
26613 * @cfg {Number} pageSize
26614 * The number of records to display per page (defaults to 20)
26618 * @cfg {String} displayMsg
26619 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26621 displayMsg : 'Displaying {0} - {1} of {2}',
26623 * @cfg {String} emptyMsg
26624 * The message to display when no records are found (defaults to "No data to display")
26626 emptyMsg : 'No data to display',
26628 * Customizable piece of the default paging text (defaults to "Page")
26631 beforePageText : "Page",
26633 * Customizable piece of the default paging text (defaults to "of %0")
26636 afterPageText : "of {0}",
26638 * Customizable piece of the default paging text (defaults to "First Page")
26641 firstText : "First Page",
26643 * Customizable piece of the default paging text (defaults to "Previous Page")
26646 prevText : "Previous Page",
26648 * Customizable piece of the default paging text (defaults to "Next Page")
26651 nextText : "Next Page",
26653 * Customizable piece of the default paging text (defaults to "Last Page")
26656 lastText : "Last Page",
26658 * Customizable piece of the default paging text (defaults to "Refresh")
26661 refreshText : "Refresh",
26665 onRender : function(ct, position)
26667 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26668 this.navgroup.parentId = this.id;
26669 this.navgroup.onRender(this.el, null);
26670 // add the buttons to the navgroup
26672 if(this.displayInfo){
26673 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26674 this.displayEl = this.el.select('.x-paging-info', true).first();
26675 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26676 // this.displayEl = navel.el.select('span',true).first();
26682 Roo.each(_this.buttons, function(e){ // this might need to use render????
26683 Roo.factory(e).render(_this.el);
26687 Roo.each(_this.toolbarItems, function(e) {
26688 _this.navgroup.addItem(e);
26692 this.first = this.navgroup.addItem({
26693 tooltip: this.firstText,
26694 cls: "prev btn-outline-secondary",
26695 html : ' <i class="fa fa-step-backward"></i>',
26697 preventDefault: true,
26698 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26701 this.prev = this.navgroup.addItem({
26702 tooltip: this.prevText,
26703 cls: "prev btn-outline-secondary",
26704 html : ' <i class="fa fa-backward"></i>',
26706 preventDefault: true,
26707 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26709 //this.addSeparator();
26712 var field = this.navgroup.addItem( {
26714 cls : 'x-paging-position btn-outline-secondary',
26716 html : this.beforePageText +
26717 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26718 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26721 this.field = field.el.select('input', true).first();
26722 this.field.on("keydown", this.onPagingKeydown, this);
26723 this.field.on("focus", function(){this.dom.select();});
26726 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26727 //this.field.setHeight(18);
26728 //this.addSeparator();
26729 this.next = this.navgroup.addItem({
26730 tooltip: this.nextText,
26731 cls: "next btn-outline-secondary",
26732 html : ' <i class="fa fa-forward"></i>',
26734 preventDefault: true,
26735 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26737 this.last = this.navgroup.addItem({
26738 tooltip: this.lastText,
26739 html : ' <i class="fa fa-step-forward"></i>',
26740 cls: "next btn-outline-secondary",
26742 preventDefault: true,
26743 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26745 //this.addSeparator();
26746 this.loading = this.navgroup.addItem({
26747 tooltip: this.refreshText,
26748 cls: "btn-outline-secondary",
26749 html : ' <i class="fa fa-refresh"></i>',
26750 preventDefault: true,
26751 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26757 updateInfo : function(){
26758 if(this.displayEl){
26759 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26760 var msg = count == 0 ?
26764 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26766 this.displayEl.update(msg);
26771 onLoad : function(ds, r, o)
26773 this.cursor = o.params.start ? o.params.start : 0;
26775 var d = this.getPageData(),
26780 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26781 this.field.dom.value = ap;
26782 this.first.setDisabled(ap == 1);
26783 this.prev.setDisabled(ap == 1);
26784 this.next.setDisabled(ap == ps);
26785 this.last.setDisabled(ap == ps);
26786 this.loading.enable();
26791 getPageData : function(){
26792 var total = this.ds.getTotalCount();
26795 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26796 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26801 onLoadError : function(){
26802 this.loading.enable();
26806 onPagingKeydown : function(e){
26807 var k = e.getKey();
26808 var d = this.getPageData();
26810 var v = this.field.dom.value, pageNum;
26811 if(!v || isNaN(pageNum = parseInt(v, 10))){
26812 this.field.dom.value = d.activePage;
26815 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26816 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26819 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))
26821 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26822 this.field.dom.value = pageNum;
26823 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26826 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26828 var v = this.field.dom.value, pageNum;
26829 var increment = (e.shiftKey) ? 10 : 1;
26830 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26833 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26834 this.field.dom.value = d.activePage;
26837 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26839 this.field.dom.value = parseInt(v, 10) + increment;
26840 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26841 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26848 beforeLoad : function(){
26850 this.loading.disable();
26855 onClick : function(which){
26864 ds.load({params:{start: 0, limit: this.pageSize}});
26867 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26870 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26873 var total = ds.getTotalCount();
26874 var extra = total % this.pageSize;
26875 var lastStart = extra ? (total - extra) : total-this.pageSize;
26876 ds.load({params:{start: lastStart, limit: this.pageSize}});
26879 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26885 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26886 * @param {Roo.data.Store} store The data store to unbind
26888 unbind : function(ds){
26889 ds.un("beforeload", this.beforeLoad, this);
26890 ds.un("load", this.onLoad, this);
26891 ds.un("loadexception", this.onLoadError, this);
26892 ds.un("remove", this.updateInfo, this);
26893 ds.un("add", this.updateInfo, this);
26894 this.ds = undefined;
26898 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26899 * @param {Roo.data.Store} store The data store to bind
26901 bind : function(ds){
26902 ds.on("beforeload", this.beforeLoad, this);
26903 ds.on("load", this.onLoad, this);
26904 ds.on("loadexception", this.onLoadError, this);
26905 ds.on("remove", this.updateInfo, this);
26906 ds.on("add", this.updateInfo, this);
26917 * @class Roo.bootstrap.MessageBar
26918 * @extends Roo.bootstrap.Component
26919 * Bootstrap MessageBar class
26920 * @cfg {String} html contents of the MessageBar
26921 * @cfg {String} weight (info | success | warning | danger) default info
26922 * @cfg {String} beforeClass insert the bar before the given class
26923 * @cfg {Boolean} closable (true | false) default false
26924 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26927 * Create a new Element
26928 * @param {Object} config The config object
26931 Roo.bootstrap.MessageBar = function(config){
26932 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26935 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26941 beforeClass: 'bootstrap-sticky-wrap',
26943 getAutoCreate : function(){
26947 cls: 'alert alert-dismissable alert-' + this.weight,
26952 html: this.html || ''
26958 cfg.cls += ' alert-messages-fixed';
26972 onRender : function(ct, position)
26974 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26977 var cfg = Roo.apply({}, this.getAutoCreate());
26981 cfg.cls += ' ' + this.cls;
26984 cfg.style = this.style;
26986 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26988 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26991 this.el.select('>button.close').on('click', this.hide, this);
26997 if (!this.rendered) {
27003 this.fireEvent('show', this);
27009 if (!this.rendered) {
27015 this.fireEvent('hide', this);
27018 update : function()
27020 // var e = this.el.dom.firstChild;
27022 // if(this.closable){
27023 // e = e.nextSibling;
27026 // e.data = this.html || '';
27028 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27044 * @class Roo.bootstrap.Graph
27045 * @extends Roo.bootstrap.Component
27046 * Bootstrap Graph class
27050 @cfg {String} graphtype bar | vbar | pie
27051 @cfg {number} g_x coodinator | centre x (pie)
27052 @cfg {number} g_y coodinator | centre y (pie)
27053 @cfg {number} g_r radius (pie)
27054 @cfg {number} g_height height of the chart (respected by all elements in the set)
27055 @cfg {number} g_width width of the chart (respected by all elements in the set)
27056 @cfg {Object} title The title of the chart
27059 -opts (object) options for the chart
27061 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27062 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27064 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.
27065 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27067 o stretch (boolean)
27069 -opts (object) options for the pie
27072 o startAngle (number)
27073 o endAngle (number)
27077 * Create a new Input
27078 * @param {Object} config The config object
27081 Roo.bootstrap.Graph = function(config){
27082 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27088 * The img click event for the img.
27089 * @param {Roo.EventObject} e
27095 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27106 //g_colors: this.colors,
27113 getAutoCreate : function(){
27124 onRender : function(ct,position){
27127 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27129 if (typeof(Raphael) == 'undefined') {
27130 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27134 this.raphael = Raphael(this.el.dom);
27136 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27137 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27138 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27139 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27141 r.text(160, 10, "Single Series Chart").attr(txtattr);
27142 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27143 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27144 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27146 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27147 r.barchart(330, 10, 300, 220, data1);
27148 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27149 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27152 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27153 // r.barchart(30, 30, 560, 250, xdata, {
27154 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27155 // axis : "0 0 1 1",
27156 // axisxlabels : xdata
27157 // //yvalues : cols,
27160 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27162 // this.load(null,xdata,{
27163 // axis : "0 0 1 1",
27164 // axisxlabels : xdata
27169 load : function(graphtype,xdata,opts)
27171 this.raphael.clear();
27173 graphtype = this.graphtype;
27178 var r = this.raphael,
27179 fin = function () {
27180 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27182 fout = function () {
27183 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27185 pfin = function() {
27186 this.sector.stop();
27187 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27190 this.label[0].stop();
27191 this.label[0].attr({ r: 7.5 });
27192 this.label[1].attr({ "font-weight": 800 });
27195 pfout = function() {
27196 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27199 this.label[0].animate({ r: 5 }, 500, "bounce");
27200 this.label[1].attr({ "font-weight": 400 });
27206 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27209 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27212 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27213 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27215 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27222 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27227 setTitle: function(o)
27232 initEvents: function() {
27235 this.el.on('click', this.onClick, this);
27239 onClick : function(e)
27241 Roo.log('img onclick');
27242 this.fireEvent('click', this, e);
27254 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27257 * @class Roo.bootstrap.dash.NumberBox
27258 * @extends Roo.bootstrap.Component
27259 * Bootstrap NumberBox class
27260 * @cfg {String} headline Box headline
27261 * @cfg {String} content Box content
27262 * @cfg {String} icon Box icon
27263 * @cfg {String} footer Footer text
27264 * @cfg {String} fhref Footer href
27267 * Create a new NumberBox
27268 * @param {Object} config The config object
27272 Roo.bootstrap.dash.NumberBox = function(config){
27273 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27277 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27286 getAutoCreate : function(){
27290 cls : 'small-box ',
27298 cls : 'roo-headline',
27299 html : this.headline
27303 cls : 'roo-content',
27304 html : this.content
27318 cls : 'ion ' + this.icon
27327 cls : 'small-box-footer',
27328 href : this.fhref || '#',
27332 cfg.cn.push(footer);
27339 onRender : function(ct,position){
27340 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27347 setHeadline: function (value)
27349 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27352 setFooter: function (value, href)
27354 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27357 this.el.select('a.small-box-footer',true).first().attr('href', href);
27362 setContent: function (value)
27364 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27367 initEvents: function()
27381 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27384 * @class Roo.bootstrap.dash.TabBox
27385 * @extends Roo.bootstrap.Component
27386 * Bootstrap TabBox class
27387 * @cfg {String} title Title of the TabBox
27388 * @cfg {String} icon Icon of the TabBox
27389 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27390 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27393 * Create a new TabBox
27394 * @param {Object} config The config object
27398 Roo.bootstrap.dash.TabBox = function(config){
27399 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27404 * When a pane is added
27405 * @param {Roo.bootstrap.dash.TabPane} pane
27409 * @event activatepane
27410 * When a pane is activated
27411 * @param {Roo.bootstrap.dash.TabPane} pane
27413 "activatepane" : true
27421 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27426 tabScrollable : false,
27428 getChildContainer : function()
27430 return this.el.select('.tab-content', true).first();
27433 getAutoCreate : function(){
27437 cls: 'pull-left header',
27445 cls: 'fa ' + this.icon
27451 cls: 'nav nav-tabs pull-right',
27457 if(this.tabScrollable){
27464 cls: 'nav nav-tabs pull-right',
27475 cls: 'nav-tabs-custom',
27480 cls: 'tab-content no-padding',
27488 initEvents : function()
27490 //Roo.log('add add pane handler');
27491 this.on('addpane', this.onAddPane, this);
27494 * Updates the box title
27495 * @param {String} html to set the title to.
27497 setTitle : function(value)
27499 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27501 onAddPane : function(pane)
27503 this.panes.push(pane);
27504 //Roo.log('addpane');
27506 // tabs are rendere left to right..
27507 if(!this.showtabs){
27511 var ctr = this.el.select('.nav-tabs', true).first();
27514 var existing = ctr.select('.nav-tab',true);
27515 var qty = existing.getCount();;
27518 var tab = ctr.createChild({
27520 cls : 'nav-tab' + (qty ? '' : ' active'),
27528 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27531 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27533 pane.el.addClass('active');
27538 onTabClick : function(ev,un,ob,pane)
27540 //Roo.log('tab - prev default');
27541 ev.preventDefault();
27544 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27545 pane.tab.addClass('active');
27546 //Roo.log(pane.title);
27547 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27548 // technically we should have a deactivate event.. but maybe add later.
27549 // and it should not de-activate the selected tab...
27550 this.fireEvent('activatepane', pane);
27551 pane.el.addClass('active');
27552 pane.fireEvent('activate');
27557 getActivePane : function()
27560 Roo.each(this.panes, function(p) {
27561 if(p.el.hasClass('active')){
27582 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27584 * @class Roo.bootstrap.TabPane
27585 * @extends Roo.bootstrap.Component
27586 * Bootstrap TabPane class
27587 * @cfg {Boolean} active (false | true) Default false
27588 * @cfg {String} title title of panel
27592 * Create a new TabPane
27593 * @param {Object} config The config object
27596 Roo.bootstrap.dash.TabPane = function(config){
27597 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27603 * When a pane is activated
27604 * @param {Roo.bootstrap.dash.TabPane} pane
27611 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27616 // the tabBox that this is attached to.
27619 getAutoCreate : function()
27627 cfg.cls += ' active';
27632 initEvents : function()
27634 //Roo.log('trigger add pane handler');
27635 this.parent().fireEvent('addpane', this)
27639 * Updates the tab title
27640 * @param {String} html to set the title to.
27642 setTitle: function(str)
27648 this.tab.select('a', true).first().dom.innerHTML = str;
27665 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27668 * @class Roo.bootstrap.menu.Menu
27669 * @extends Roo.bootstrap.Component
27670 * Bootstrap Menu class - container for Menu
27671 * @cfg {String} html Text of the menu
27672 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27673 * @cfg {String} icon Font awesome icon
27674 * @cfg {String} pos Menu align to (top | bottom) default bottom
27678 * Create a new Menu
27679 * @param {Object} config The config object
27683 Roo.bootstrap.menu.Menu = function(config){
27684 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27688 * @event beforeshow
27689 * Fires before this menu is displayed
27690 * @param {Roo.bootstrap.menu.Menu} this
27694 * @event beforehide
27695 * Fires before this menu is hidden
27696 * @param {Roo.bootstrap.menu.Menu} this
27701 * Fires after this menu is displayed
27702 * @param {Roo.bootstrap.menu.Menu} this
27707 * Fires after this menu is hidden
27708 * @param {Roo.bootstrap.menu.Menu} this
27713 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27714 * @param {Roo.bootstrap.menu.Menu} this
27715 * @param {Roo.EventObject} e
27722 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27726 weight : 'default',
27731 getChildContainer : function() {
27732 if(this.isSubMenu){
27736 return this.el.select('ul.dropdown-menu', true).first();
27739 getAutoCreate : function()
27744 cls : 'roo-menu-text',
27752 cls : 'fa ' + this.icon
27763 cls : 'dropdown-button btn btn-' + this.weight,
27768 cls : 'dropdown-toggle btn btn-' + this.weight,
27778 cls : 'dropdown-menu'
27784 if(this.pos == 'top'){
27785 cfg.cls += ' dropup';
27788 if(this.isSubMenu){
27791 cls : 'dropdown-menu'
27798 onRender : function(ct, position)
27800 this.isSubMenu = ct.hasClass('dropdown-submenu');
27802 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27805 initEvents : function()
27807 if(this.isSubMenu){
27811 this.hidden = true;
27813 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27814 this.triggerEl.on('click', this.onTriggerPress, this);
27816 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27817 this.buttonEl.on('click', this.onClick, this);
27823 if(this.isSubMenu){
27827 return this.el.select('ul.dropdown-menu', true).first();
27830 onClick : function(e)
27832 this.fireEvent("click", this, e);
27835 onTriggerPress : function(e)
27837 if (this.isVisible()) {
27844 isVisible : function(){
27845 return !this.hidden;
27850 this.fireEvent("beforeshow", this);
27852 this.hidden = false;
27853 this.el.addClass('open');
27855 Roo.get(document).on("mouseup", this.onMouseUp, this);
27857 this.fireEvent("show", this);
27864 this.fireEvent("beforehide", this);
27866 this.hidden = true;
27867 this.el.removeClass('open');
27869 Roo.get(document).un("mouseup", this.onMouseUp);
27871 this.fireEvent("hide", this);
27874 onMouseUp : function()
27888 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27891 * @class Roo.bootstrap.menu.Item
27892 * @extends Roo.bootstrap.Component
27893 * Bootstrap MenuItem class
27894 * @cfg {Boolean} submenu (true | false) default false
27895 * @cfg {String} html text of the item
27896 * @cfg {String} href the link
27897 * @cfg {Boolean} disable (true | false) default false
27898 * @cfg {Boolean} preventDefault (true | false) default true
27899 * @cfg {String} icon Font awesome icon
27900 * @cfg {String} pos Submenu align to (left | right) default right
27904 * Create a new Item
27905 * @param {Object} config The config object
27909 Roo.bootstrap.menu.Item = function(config){
27910 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27914 * Fires when the mouse is hovering over this menu
27915 * @param {Roo.bootstrap.menu.Item} this
27916 * @param {Roo.EventObject} e
27921 * Fires when the mouse exits this menu
27922 * @param {Roo.bootstrap.menu.Item} this
27923 * @param {Roo.EventObject} e
27929 * The raw click event for the entire grid.
27930 * @param {Roo.EventObject} e
27936 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27941 preventDefault: true,
27946 getAutoCreate : function()
27951 cls : 'roo-menu-item-text',
27959 cls : 'fa ' + this.icon
27968 href : this.href || '#',
27975 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27979 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27981 if(this.pos == 'left'){
27982 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27989 initEvents : function()
27991 this.el.on('mouseover', this.onMouseOver, this);
27992 this.el.on('mouseout', this.onMouseOut, this);
27994 this.el.select('a', true).first().on('click', this.onClick, this);
27998 onClick : function(e)
28000 if(this.preventDefault){
28001 e.preventDefault();
28004 this.fireEvent("click", this, e);
28007 onMouseOver : function(e)
28009 if(this.submenu && this.pos == 'left'){
28010 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28013 this.fireEvent("mouseover", this, e);
28016 onMouseOut : function(e)
28018 this.fireEvent("mouseout", this, e);
28030 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28033 * @class Roo.bootstrap.menu.Separator
28034 * @extends Roo.bootstrap.Component
28035 * Bootstrap Separator class
28038 * Create a new Separator
28039 * @param {Object} config The config object
28043 Roo.bootstrap.menu.Separator = function(config){
28044 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28047 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28049 getAutoCreate : function(){
28070 * @class Roo.bootstrap.Tooltip
28071 * Bootstrap Tooltip class
28072 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28073 * to determine which dom element triggers the tooltip.
28075 * It needs to add support for additional attributes like tooltip-position
28078 * Create a new Toolti
28079 * @param {Object} config The config object
28082 Roo.bootstrap.Tooltip = function(config){
28083 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28085 this.alignment = Roo.bootstrap.Tooltip.alignment;
28087 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28088 this.alignment = config.alignment;
28093 Roo.apply(Roo.bootstrap.Tooltip, {
28095 * @function init initialize tooltip monitoring.
28099 currentTip : false,
28100 currentRegion : false,
28106 Roo.get(document).on('mouseover', this.enter ,this);
28107 Roo.get(document).on('mouseout', this.leave, this);
28110 this.currentTip = new Roo.bootstrap.Tooltip();
28113 enter : function(ev)
28115 var dom = ev.getTarget();
28117 //Roo.log(['enter',dom]);
28118 var el = Roo.fly(dom);
28119 if (this.currentEl) {
28121 //Roo.log(this.currentEl);
28122 //Roo.log(this.currentEl.contains(dom));
28123 if (this.currentEl == el) {
28126 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28132 if (this.currentTip.el) {
28133 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28137 if(!el || el.dom == document){
28143 // you can not look for children, as if el is the body.. then everythign is the child..
28144 if (!el.attr('tooltip')) { //
28145 if (!el.select("[tooltip]").elements.length) {
28148 // is the mouse over this child...?
28149 bindEl = el.select("[tooltip]").first();
28150 var xy = ev.getXY();
28151 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28152 //Roo.log("not in region.");
28155 //Roo.log("child element over..");
28158 this.currentEl = bindEl;
28159 this.currentTip.bind(bindEl);
28160 this.currentRegion = Roo.lib.Region.getRegion(dom);
28161 this.currentTip.enter();
28164 leave : function(ev)
28166 var dom = ev.getTarget();
28167 //Roo.log(['leave',dom]);
28168 if (!this.currentEl) {
28173 if (dom != this.currentEl.dom) {
28176 var xy = ev.getXY();
28177 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28180 // only activate leave if mouse cursor is outside... bounding box..
28185 if (this.currentTip) {
28186 this.currentTip.leave();
28188 //Roo.log('clear currentEl');
28189 this.currentEl = false;
28194 'left' : ['r-l', [-2,0], 'right'],
28195 'right' : ['l-r', [2,0], 'left'],
28196 'bottom' : ['t-b', [0,2], 'top'],
28197 'top' : [ 'b-t', [0,-2], 'bottom']
28203 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28208 delay : null, // can be { show : 300 , hide: 500}
28212 hoverState : null, //???
28214 placement : 'bottom',
28218 getAutoCreate : function(){
28225 cls : 'tooltip-arrow'
28228 cls : 'tooltip-inner'
28235 bind : function(el)
28241 enter : function () {
28243 if (this.timeout != null) {
28244 clearTimeout(this.timeout);
28247 this.hoverState = 'in';
28248 //Roo.log("enter - show");
28249 if (!this.delay || !this.delay.show) {
28254 this.timeout = setTimeout(function () {
28255 if (_t.hoverState == 'in') {
28258 }, this.delay.show);
28262 clearTimeout(this.timeout);
28264 this.hoverState = 'out';
28265 if (!this.delay || !this.delay.hide) {
28271 this.timeout = setTimeout(function () {
28272 //Roo.log("leave - timeout");
28274 if (_t.hoverState == 'out') {
28276 Roo.bootstrap.Tooltip.currentEl = false;
28281 show : function (msg)
28284 this.render(document.body);
28287 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28289 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28291 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28293 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28295 var placement = typeof this.placement == 'function' ?
28296 this.placement.call(this, this.el, on_el) :
28299 var autoToken = /\s?auto?\s?/i;
28300 var autoPlace = autoToken.test(placement);
28302 placement = placement.replace(autoToken, '') || 'top';
28306 //this.el.setXY([0,0]);
28308 //this.el.dom.style.display='block';
28310 //this.el.appendTo(on_el);
28312 var p = this.getPosition();
28313 var box = this.el.getBox();
28319 var align = this.alignment[placement];
28321 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28323 if(placement == 'top' || placement == 'bottom'){
28325 placement = 'right';
28328 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28329 placement = 'left';
28332 var scroll = Roo.select('body', true).first().getScroll();
28334 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28338 align = this.alignment[placement];
28341 this.el.alignTo(this.bindEl, align[0],align[1]);
28342 //var arrow = this.el.select('.arrow',true).first();
28343 //arrow.set(align[2],
28345 this.el.addClass(placement);
28347 this.el.addClass('in fade');
28349 this.hoverState = null;
28351 if (this.el.hasClass('fade')) {
28362 //this.el.setXY([0,0]);
28363 this.el.removeClass('in');
28379 * @class Roo.bootstrap.LocationPicker
28380 * @extends Roo.bootstrap.Component
28381 * Bootstrap LocationPicker class
28382 * @cfg {Number} latitude Position when init default 0
28383 * @cfg {Number} longitude Position when init default 0
28384 * @cfg {Number} zoom default 15
28385 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28386 * @cfg {Boolean} mapTypeControl default false
28387 * @cfg {Boolean} disableDoubleClickZoom default false
28388 * @cfg {Boolean} scrollwheel default true
28389 * @cfg {Boolean} streetViewControl default false
28390 * @cfg {Number} radius default 0
28391 * @cfg {String} locationName
28392 * @cfg {Boolean} draggable default true
28393 * @cfg {Boolean} enableAutocomplete default false
28394 * @cfg {Boolean} enableReverseGeocode default true
28395 * @cfg {String} markerTitle
28398 * Create a new LocationPicker
28399 * @param {Object} config The config object
28403 Roo.bootstrap.LocationPicker = function(config){
28405 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28410 * Fires when the picker initialized.
28411 * @param {Roo.bootstrap.LocationPicker} this
28412 * @param {Google Location} location
28416 * @event positionchanged
28417 * Fires when the picker position changed.
28418 * @param {Roo.bootstrap.LocationPicker} this
28419 * @param {Google Location} location
28421 positionchanged : true,
28424 * Fires when the map resize.
28425 * @param {Roo.bootstrap.LocationPicker} this
28430 * Fires when the map show.
28431 * @param {Roo.bootstrap.LocationPicker} this
28436 * Fires when the map hide.
28437 * @param {Roo.bootstrap.LocationPicker} this
28442 * Fires when click the map.
28443 * @param {Roo.bootstrap.LocationPicker} this
28444 * @param {Map event} e
28448 * @event mapRightClick
28449 * Fires when right click the map.
28450 * @param {Roo.bootstrap.LocationPicker} this
28451 * @param {Map event} e
28453 mapRightClick : true,
28455 * @event markerClick
28456 * Fires when click the marker.
28457 * @param {Roo.bootstrap.LocationPicker} this
28458 * @param {Map event} e
28460 markerClick : true,
28462 * @event markerRightClick
28463 * Fires when right click the marker.
28464 * @param {Roo.bootstrap.LocationPicker} this
28465 * @param {Map event} e
28467 markerRightClick : true,
28469 * @event OverlayViewDraw
28470 * Fires when OverlayView Draw
28471 * @param {Roo.bootstrap.LocationPicker} this
28473 OverlayViewDraw : true,
28475 * @event OverlayViewOnAdd
28476 * Fires when OverlayView Draw
28477 * @param {Roo.bootstrap.LocationPicker} this
28479 OverlayViewOnAdd : true,
28481 * @event OverlayViewOnRemove
28482 * Fires when OverlayView Draw
28483 * @param {Roo.bootstrap.LocationPicker} this
28485 OverlayViewOnRemove : true,
28487 * @event OverlayViewShow
28488 * Fires when OverlayView Draw
28489 * @param {Roo.bootstrap.LocationPicker} this
28490 * @param {Pixel} cpx
28492 OverlayViewShow : true,
28494 * @event OverlayViewHide
28495 * Fires when OverlayView Draw
28496 * @param {Roo.bootstrap.LocationPicker} this
28498 OverlayViewHide : true,
28500 * @event loadexception
28501 * Fires when load google lib failed.
28502 * @param {Roo.bootstrap.LocationPicker} this
28504 loadexception : true
28509 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28511 gMapContext: false,
28517 mapTypeControl: false,
28518 disableDoubleClickZoom: false,
28520 streetViewControl: false,
28524 enableAutocomplete: false,
28525 enableReverseGeocode: true,
28528 getAutoCreate: function()
28533 cls: 'roo-location-picker'
28539 initEvents: function(ct, position)
28541 if(!this.el.getWidth() || this.isApplied()){
28545 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28550 initial: function()
28552 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28553 this.fireEvent('loadexception', this);
28557 if(!this.mapTypeId){
28558 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28561 this.gMapContext = this.GMapContext();
28563 this.initOverlayView();
28565 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28569 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28570 _this.setPosition(_this.gMapContext.marker.position);
28573 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28574 _this.fireEvent('mapClick', this, event);
28578 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28579 _this.fireEvent('mapRightClick', this, event);
28583 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28584 _this.fireEvent('markerClick', this, event);
28588 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28589 _this.fireEvent('markerRightClick', this, event);
28593 this.setPosition(this.gMapContext.location);
28595 this.fireEvent('initial', this, this.gMapContext.location);
28598 initOverlayView: function()
28602 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28606 _this.fireEvent('OverlayViewDraw', _this);
28611 _this.fireEvent('OverlayViewOnAdd', _this);
28614 onRemove: function()
28616 _this.fireEvent('OverlayViewOnRemove', _this);
28619 show: function(cpx)
28621 _this.fireEvent('OverlayViewShow', _this, cpx);
28626 _this.fireEvent('OverlayViewHide', _this);
28632 fromLatLngToContainerPixel: function(event)
28634 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28637 isApplied: function()
28639 return this.getGmapContext() == false ? false : true;
28642 getGmapContext: function()
28644 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28647 GMapContext: function()
28649 var position = new google.maps.LatLng(this.latitude, this.longitude);
28651 var _map = new google.maps.Map(this.el.dom, {
28654 mapTypeId: this.mapTypeId,
28655 mapTypeControl: this.mapTypeControl,
28656 disableDoubleClickZoom: this.disableDoubleClickZoom,
28657 scrollwheel: this.scrollwheel,
28658 streetViewControl: this.streetViewControl,
28659 locationName: this.locationName,
28660 draggable: this.draggable,
28661 enableAutocomplete: this.enableAutocomplete,
28662 enableReverseGeocode: this.enableReverseGeocode
28665 var _marker = new google.maps.Marker({
28666 position: position,
28668 title: this.markerTitle,
28669 draggable: this.draggable
28676 location: position,
28677 radius: this.radius,
28678 locationName: this.locationName,
28679 addressComponents: {
28680 formatted_address: null,
28681 addressLine1: null,
28682 addressLine2: null,
28684 streetNumber: null,
28688 stateOrProvince: null
28691 domContainer: this.el.dom,
28692 geodecoder: new google.maps.Geocoder()
28696 drawCircle: function(center, radius, options)
28698 if (this.gMapContext.circle != null) {
28699 this.gMapContext.circle.setMap(null);
28703 options = Roo.apply({}, options, {
28704 strokeColor: "#0000FF",
28705 strokeOpacity: .35,
28707 fillColor: "#0000FF",
28711 options.map = this.gMapContext.map;
28712 options.radius = radius;
28713 options.center = center;
28714 this.gMapContext.circle = new google.maps.Circle(options);
28715 return this.gMapContext.circle;
28721 setPosition: function(location)
28723 this.gMapContext.location = location;
28724 this.gMapContext.marker.setPosition(location);
28725 this.gMapContext.map.panTo(location);
28726 this.drawCircle(location, this.gMapContext.radius, {});
28730 if (this.gMapContext.settings.enableReverseGeocode) {
28731 this.gMapContext.geodecoder.geocode({
28732 latLng: this.gMapContext.location
28733 }, function(results, status) {
28735 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28736 _this.gMapContext.locationName = results[0].formatted_address;
28737 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28739 _this.fireEvent('positionchanged', this, location);
28746 this.fireEvent('positionchanged', this, location);
28751 google.maps.event.trigger(this.gMapContext.map, "resize");
28753 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28755 this.fireEvent('resize', this);
28758 setPositionByLatLng: function(latitude, longitude)
28760 this.setPosition(new google.maps.LatLng(latitude, longitude));
28763 getCurrentPosition: function()
28766 latitude: this.gMapContext.location.lat(),
28767 longitude: this.gMapContext.location.lng()
28771 getAddressName: function()
28773 return this.gMapContext.locationName;
28776 getAddressComponents: function()
28778 return this.gMapContext.addressComponents;
28781 address_component_from_google_geocode: function(address_components)
28785 for (var i = 0; i < address_components.length; i++) {
28786 var component = address_components[i];
28787 if (component.types.indexOf("postal_code") >= 0) {
28788 result.postalCode = component.short_name;
28789 } else if (component.types.indexOf("street_number") >= 0) {
28790 result.streetNumber = component.short_name;
28791 } else if (component.types.indexOf("route") >= 0) {
28792 result.streetName = component.short_name;
28793 } else if (component.types.indexOf("neighborhood") >= 0) {
28794 result.city = component.short_name;
28795 } else if (component.types.indexOf("locality") >= 0) {
28796 result.city = component.short_name;
28797 } else if (component.types.indexOf("sublocality") >= 0) {
28798 result.district = component.short_name;
28799 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28800 result.stateOrProvince = component.short_name;
28801 } else if (component.types.indexOf("country") >= 0) {
28802 result.country = component.short_name;
28806 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28807 result.addressLine2 = "";
28811 setZoomLevel: function(zoom)
28813 this.gMapContext.map.setZoom(zoom);
28826 this.fireEvent('show', this);
28837 this.fireEvent('hide', this);
28842 Roo.apply(Roo.bootstrap.LocationPicker, {
28844 OverlayView : function(map, options)
28846 options = options || {};
28853 * @class Roo.bootstrap.Alert
28854 * @extends Roo.bootstrap.Component
28855 * Bootstrap Alert class - shows an alert area box
28857 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28858 Enter a valid email address
28861 * @cfg {String} title The title of alert
28862 * @cfg {String} html The content of alert
28863 * @cfg {String} weight ( success | info | warning | danger )
28864 * @cfg {String} faicon font-awesomeicon
28867 * Create a new alert
28868 * @param {Object} config The config object
28872 Roo.bootstrap.Alert = function(config){
28873 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28877 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28884 getAutoCreate : function()
28893 cls : 'roo-alert-icon'
28898 cls : 'roo-alert-title',
28903 cls : 'roo-alert-text',
28910 cfg.cn[0].cls += ' fa ' + this.faicon;
28914 cfg.cls += ' alert-' + this.weight;
28920 initEvents: function()
28922 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28925 setTitle : function(str)
28927 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28930 setText : function(str)
28932 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28935 setWeight : function(weight)
28938 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28941 this.weight = weight;
28943 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28946 setIcon : function(icon)
28949 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28952 this.faicon = icon;
28954 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28975 * @class Roo.bootstrap.UploadCropbox
28976 * @extends Roo.bootstrap.Component
28977 * Bootstrap UploadCropbox class
28978 * @cfg {String} emptyText show when image has been loaded
28979 * @cfg {String} rotateNotify show when image too small to rotate
28980 * @cfg {Number} errorTimeout default 3000
28981 * @cfg {Number} minWidth default 300
28982 * @cfg {Number} minHeight default 300
28983 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28984 * @cfg {Boolean} isDocument (true|false) default false
28985 * @cfg {String} url action url
28986 * @cfg {String} paramName default 'imageUpload'
28987 * @cfg {String} method default POST
28988 * @cfg {Boolean} loadMask (true|false) default true
28989 * @cfg {Boolean} loadingText default 'Loading...'
28992 * Create a new UploadCropbox
28993 * @param {Object} config The config object
28996 Roo.bootstrap.UploadCropbox = function(config){
28997 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29001 * @event beforeselectfile
29002 * Fire before select file
29003 * @param {Roo.bootstrap.UploadCropbox} this
29005 "beforeselectfile" : true,
29008 * Fire after initEvent
29009 * @param {Roo.bootstrap.UploadCropbox} this
29014 * Fire after initEvent
29015 * @param {Roo.bootstrap.UploadCropbox} this
29016 * @param {String} data
29021 * Fire when preparing the file data
29022 * @param {Roo.bootstrap.UploadCropbox} this
29023 * @param {Object} file
29028 * Fire when get exception
29029 * @param {Roo.bootstrap.UploadCropbox} this
29030 * @param {XMLHttpRequest} xhr
29032 "exception" : true,
29034 * @event beforeloadcanvas
29035 * Fire before load the canvas
29036 * @param {Roo.bootstrap.UploadCropbox} this
29037 * @param {String} src
29039 "beforeloadcanvas" : true,
29042 * Fire when trash image
29043 * @param {Roo.bootstrap.UploadCropbox} this
29048 * Fire when download the image
29049 * @param {Roo.bootstrap.UploadCropbox} this
29053 * @event footerbuttonclick
29054 * Fire when footerbuttonclick
29055 * @param {Roo.bootstrap.UploadCropbox} this
29056 * @param {String} type
29058 "footerbuttonclick" : true,
29062 * @param {Roo.bootstrap.UploadCropbox} this
29067 * Fire when rotate the image
29068 * @param {Roo.bootstrap.UploadCropbox} this
29069 * @param {String} pos
29074 * Fire when inspect the file
29075 * @param {Roo.bootstrap.UploadCropbox} this
29076 * @param {Object} file
29081 * Fire when xhr upload the file
29082 * @param {Roo.bootstrap.UploadCropbox} this
29083 * @param {Object} data
29088 * Fire when arrange the file data
29089 * @param {Roo.bootstrap.UploadCropbox} this
29090 * @param {Object} formData
29095 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29098 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29100 emptyText : 'Click to upload image',
29101 rotateNotify : 'Image is too small to rotate',
29102 errorTimeout : 3000,
29116 cropType : 'image/jpeg',
29118 canvasLoaded : false,
29119 isDocument : false,
29121 paramName : 'imageUpload',
29123 loadingText : 'Loading...',
29126 getAutoCreate : function()
29130 cls : 'roo-upload-cropbox',
29134 cls : 'roo-upload-cropbox-selector',
29139 cls : 'roo-upload-cropbox-body',
29140 style : 'cursor:pointer',
29144 cls : 'roo-upload-cropbox-preview'
29148 cls : 'roo-upload-cropbox-thumb'
29152 cls : 'roo-upload-cropbox-empty-notify',
29153 html : this.emptyText
29157 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29158 html : this.rotateNotify
29164 cls : 'roo-upload-cropbox-footer',
29167 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29177 onRender : function(ct, position)
29179 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29181 if (this.buttons.length) {
29183 Roo.each(this.buttons, function(bb) {
29185 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29187 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29193 this.maskEl = this.el;
29197 initEvents : function()
29199 this.urlAPI = (window.createObjectURL && window) ||
29200 (window.URL && URL.revokeObjectURL && URL) ||
29201 (window.webkitURL && webkitURL);
29203 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29204 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29206 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29207 this.selectorEl.hide();
29209 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29210 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29212 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29213 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29214 this.thumbEl.hide();
29216 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29217 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29219 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29220 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29221 this.errorEl.hide();
29223 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29224 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29225 this.footerEl.hide();
29227 this.setThumbBoxSize();
29233 this.fireEvent('initial', this);
29240 window.addEventListener("resize", function() { _this.resize(); } );
29242 this.bodyEl.on('click', this.beforeSelectFile, this);
29245 this.bodyEl.on('touchstart', this.onTouchStart, this);
29246 this.bodyEl.on('touchmove', this.onTouchMove, this);
29247 this.bodyEl.on('touchend', this.onTouchEnd, this);
29251 this.bodyEl.on('mousedown', this.onMouseDown, this);
29252 this.bodyEl.on('mousemove', this.onMouseMove, this);
29253 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29254 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29255 Roo.get(document).on('mouseup', this.onMouseUp, this);
29258 this.selectorEl.on('change', this.onFileSelected, this);
29264 this.baseScale = 1;
29266 this.baseRotate = 1;
29267 this.dragable = false;
29268 this.pinching = false;
29271 this.cropData = false;
29272 this.notifyEl.dom.innerHTML = this.emptyText;
29274 this.selectorEl.dom.value = '';
29278 resize : function()
29280 if(this.fireEvent('resize', this) != false){
29281 this.setThumbBoxPosition();
29282 this.setCanvasPosition();
29286 onFooterButtonClick : function(e, el, o, type)
29289 case 'rotate-left' :
29290 this.onRotateLeft(e);
29292 case 'rotate-right' :
29293 this.onRotateRight(e);
29296 this.beforeSelectFile(e);
29311 this.fireEvent('footerbuttonclick', this, type);
29314 beforeSelectFile : function(e)
29316 e.preventDefault();
29318 if(this.fireEvent('beforeselectfile', this) != false){
29319 this.selectorEl.dom.click();
29323 onFileSelected : function(e)
29325 e.preventDefault();
29327 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29331 var file = this.selectorEl.dom.files[0];
29333 if(this.fireEvent('inspect', this, file) != false){
29334 this.prepare(file);
29339 trash : function(e)
29341 this.fireEvent('trash', this);
29344 download : function(e)
29346 this.fireEvent('download', this);
29349 loadCanvas : function(src)
29351 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29355 this.imageEl = document.createElement('img');
29359 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29361 this.imageEl.src = src;
29365 onLoadCanvas : function()
29367 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29368 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29370 this.bodyEl.un('click', this.beforeSelectFile, this);
29372 this.notifyEl.hide();
29373 this.thumbEl.show();
29374 this.footerEl.show();
29376 this.baseRotateLevel();
29378 if(this.isDocument){
29379 this.setThumbBoxSize();
29382 this.setThumbBoxPosition();
29384 this.baseScaleLevel();
29390 this.canvasLoaded = true;
29393 this.maskEl.unmask();
29398 setCanvasPosition : function()
29400 if(!this.canvasEl){
29404 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29405 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29407 this.previewEl.setLeft(pw);
29408 this.previewEl.setTop(ph);
29412 onMouseDown : function(e)
29416 this.dragable = true;
29417 this.pinching = false;
29419 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29420 this.dragable = false;
29424 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29425 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29429 onMouseMove : function(e)
29433 if(!this.canvasLoaded){
29437 if (!this.dragable){
29441 var minX = Math.ceil(this.thumbEl.getLeft(true));
29442 var minY = Math.ceil(this.thumbEl.getTop(true));
29444 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29445 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29447 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29448 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29450 x = x - this.mouseX;
29451 y = y - this.mouseY;
29453 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29454 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29456 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29457 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29459 this.previewEl.setLeft(bgX);
29460 this.previewEl.setTop(bgY);
29462 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29463 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29466 onMouseUp : function(e)
29470 this.dragable = false;
29473 onMouseWheel : function(e)
29477 this.startScale = this.scale;
29479 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29481 if(!this.zoomable()){
29482 this.scale = this.startScale;
29491 zoomable : function()
29493 var minScale = this.thumbEl.getWidth() / this.minWidth;
29495 if(this.minWidth < this.minHeight){
29496 minScale = this.thumbEl.getHeight() / this.minHeight;
29499 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29500 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29504 (this.rotate == 0 || this.rotate == 180) &&
29506 width > this.imageEl.OriginWidth ||
29507 height > this.imageEl.OriginHeight ||
29508 (width < this.minWidth && height < this.minHeight)
29516 (this.rotate == 90 || this.rotate == 270) &&
29518 width > this.imageEl.OriginWidth ||
29519 height > this.imageEl.OriginHeight ||
29520 (width < this.minHeight && height < this.minWidth)
29527 !this.isDocument &&
29528 (this.rotate == 0 || this.rotate == 180) &&
29530 width < this.minWidth ||
29531 width > this.imageEl.OriginWidth ||
29532 height < this.minHeight ||
29533 height > this.imageEl.OriginHeight
29540 !this.isDocument &&
29541 (this.rotate == 90 || this.rotate == 270) &&
29543 width < this.minHeight ||
29544 width > this.imageEl.OriginWidth ||
29545 height < this.minWidth ||
29546 height > this.imageEl.OriginHeight
29556 onRotateLeft : function(e)
29558 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29560 var minScale = this.thumbEl.getWidth() / this.minWidth;
29562 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29563 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29565 this.startScale = this.scale;
29567 while (this.getScaleLevel() < minScale){
29569 this.scale = this.scale + 1;
29571 if(!this.zoomable()){
29576 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29577 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29582 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29589 this.scale = this.startScale;
29591 this.onRotateFail();
29596 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29598 if(this.isDocument){
29599 this.setThumbBoxSize();
29600 this.setThumbBoxPosition();
29601 this.setCanvasPosition();
29606 this.fireEvent('rotate', this, 'left');
29610 onRotateRight : function(e)
29612 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29614 var minScale = this.thumbEl.getWidth() / this.minWidth;
29616 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29617 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29619 this.startScale = this.scale;
29621 while (this.getScaleLevel() < minScale){
29623 this.scale = this.scale + 1;
29625 if(!this.zoomable()){
29630 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29631 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29636 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29643 this.scale = this.startScale;
29645 this.onRotateFail();
29650 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29652 if(this.isDocument){
29653 this.setThumbBoxSize();
29654 this.setThumbBoxPosition();
29655 this.setCanvasPosition();
29660 this.fireEvent('rotate', this, 'right');
29663 onRotateFail : function()
29665 this.errorEl.show(true);
29669 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29674 this.previewEl.dom.innerHTML = '';
29676 var canvasEl = document.createElement("canvas");
29678 var contextEl = canvasEl.getContext("2d");
29680 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29681 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29682 var center = this.imageEl.OriginWidth / 2;
29684 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29685 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29686 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29687 center = this.imageEl.OriginHeight / 2;
29690 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29692 contextEl.translate(center, center);
29693 contextEl.rotate(this.rotate * Math.PI / 180);
29695 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29697 this.canvasEl = document.createElement("canvas");
29699 this.contextEl = this.canvasEl.getContext("2d");
29701 switch (this.rotate) {
29704 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29705 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29707 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29712 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29713 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29715 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29716 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);
29720 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29725 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29726 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29728 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29729 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);
29733 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);
29738 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29739 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29741 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29742 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29746 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);
29753 this.previewEl.appendChild(this.canvasEl);
29755 this.setCanvasPosition();
29760 if(!this.canvasLoaded){
29764 var imageCanvas = document.createElement("canvas");
29766 var imageContext = imageCanvas.getContext("2d");
29768 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29769 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29771 var center = imageCanvas.width / 2;
29773 imageContext.translate(center, center);
29775 imageContext.rotate(this.rotate * Math.PI / 180);
29777 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29779 var canvas = document.createElement("canvas");
29781 var context = canvas.getContext("2d");
29783 canvas.width = this.minWidth;
29784 canvas.height = this.minHeight;
29786 switch (this.rotate) {
29789 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29790 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29792 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29793 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29795 var targetWidth = this.minWidth - 2 * x;
29796 var targetHeight = this.minHeight - 2 * y;
29800 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29801 scale = targetWidth / width;
29804 if(x > 0 && y == 0){
29805 scale = targetHeight / height;
29808 if(x > 0 && y > 0){
29809 scale = targetWidth / width;
29811 if(width < height){
29812 scale = targetHeight / height;
29816 context.scale(scale, scale);
29818 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29819 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29821 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29822 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29824 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29829 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29830 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29832 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29833 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29835 var targetWidth = this.minWidth - 2 * x;
29836 var targetHeight = this.minHeight - 2 * y;
29840 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29841 scale = targetWidth / width;
29844 if(x > 0 && y == 0){
29845 scale = targetHeight / height;
29848 if(x > 0 && y > 0){
29849 scale = targetWidth / width;
29851 if(width < height){
29852 scale = targetHeight / height;
29856 context.scale(scale, scale);
29858 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29859 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29861 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29862 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29864 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29866 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29871 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29872 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29874 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29875 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29877 var targetWidth = this.minWidth - 2 * x;
29878 var targetHeight = this.minHeight - 2 * y;
29882 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29883 scale = targetWidth / width;
29886 if(x > 0 && y == 0){
29887 scale = targetHeight / height;
29890 if(x > 0 && y > 0){
29891 scale = targetWidth / width;
29893 if(width < height){
29894 scale = targetHeight / height;
29898 context.scale(scale, scale);
29900 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29901 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29903 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29904 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29906 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29907 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29909 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29914 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29915 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29917 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29918 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29920 var targetWidth = this.minWidth - 2 * x;
29921 var targetHeight = this.minHeight - 2 * y;
29925 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29926 scale = targetWidth / width;
29929 if(x > 0 && y == 0){
29930 scale = targetHeight / height;
29933 if(x > 0 && y > 0){
29934 scale = targetWidth / width;
29936 if(width < height){
29937 scale = targetHeight / height;
29941 context.scale(scale, scale);
29943 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29944 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29946 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29947 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29949 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29951 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29958 this.cropData = canvas.toDataURL(this.cropType);
29960 if(this.fireEvent('crop', this, this.cropData) !== false){
29961 this.process(this.file, this.cropData);
29968 setThumbBoxSize : function()
29972 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29973 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29974 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29976 this.minWidth = width;
29977 this.minHeight = height;
29979 if(this.rotate == 90 || this.rotate == 270){
29980 this.minWidth = height;
29981 this.minHeight = width;
29986 width = Math.ceil(this.minWidth * height / this.minHeight);
29988 if(this.minWidth > this.minHeight){
29990 height = Math.ceil(this.minHeight * width / this.minWidth);
29993 this.thumbEl.setStyle({
29994 width : width + 'px',
29995 height : height + 'px'
30002 setThumbBoxPosition : function()
30004 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30005 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30007 this.thumbEl.setLeft(x);
30008 this.thumbEl.setTop(y);
30012 baseRotateLevel : function()
30014 this.baseRotate = 1;
30017 typeof(this.exif) != 'undefined' &&
30018 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30019 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30021 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30024 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30028 baseScaleLevel : function()
30032 if(this.isDocument){
30034 if(this.baseRotate == 6 || this.baseRotate == 8){
30036 height = this.thumbEl.getHeight();
30037 this.baseScale = height / this.imageEl.OriginWidth;
30039 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30040 width = this.thumbEl.getWidth();
30041 this.baseScale = width / this.imageEl.OriginHeight;
30047 height = this.thumbEl.getHeight();
30048 this.baseScale = height / this.imageEl.OriginHeight;
30050 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30051 width = this.thumbEl.getWidth();
30052 this.baseScale = width / this.imageEl.OriginWidth;
30058 if(this.baseRotate == 6 || this.baseRotate == 8){
30060 width = this.thumbEl.getHeight();
30061 this.baseScale = width / this.imageEl.OriginHeight;
30063 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30064 height = this.thumbEl.getWidth();
30065 this.baseScale = height / this.imageEl.OriginHeight;
30068 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30069 height = this.thumbEl.getWidth();
30070 this.baseScale = height / this.imageEl.OriginHeight;
30072 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30073 width = this.thumbEl.getHeight();
30074 this.baseScale = width / this.imageEl.OriginWidth;
30081 width = this.thumbEl.getWidth();
30082 this.baseScale = width / this.imageEl.OriginWidth;
30084 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30085 height = this.thumbEl.getHeight();
30086 this.baseScale = height / this.imageEl.OriginHeight;
30089 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30091 height = this.thumbEl.getHeight();
30092 this.baseScale = height / this.imageEl.OriginHeight;
30094 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30095 width = this.thumbEl.getWidth();
30096 this.baseScale = width / this.imageEl.OriginWidth;
30104 getScaleLevel : function()
30106 return this.baseScale * Math.pow(1.1, this.scale);
30109 onTouchStart : function(e)
30111 if(!this.canvasLoaded){
30112 this.beforeSelectFile(e);
30116 var touches = e.browserEvent.touches;
30122 if(touches.length == 1){
30123 this.onMouseDown(e);
30127 if(touches.length != 2){
30133 for(var i = 0, finger; finger = touches[i]; i++){
30134 coords.push(finger.pageX, finger.pageY);
30137 var x = Math.pow(coords[0] - coords[2], 2);
30138 var y = Math.pow(coords[1] - coords[3], 2);
30140 this.startDistance = Math.sqrt(x + y);
30142 this.startScale = this.scale;
30144 this.pinching = true;
30145 this.dragable = false;
30149 onTouchMove : function(e)
30151 if(!this.pinching && !this.dragable){
30155 var touches = e.browserEvent.touches;
30162 this.onMouseMove(e);
30168 for(var i = 0, finger; finger = touches[i]; i++){
30169 coords.push(finger.pageX, finger.pageY);
30172 var x = Math.pow(coords[0] - coords[2], 2);
30173 var y = Math.pow(coords[1] - coords[3], 2);
30175 this.endDistance = Math.sqrt(x + y);
30177 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30179 if(!this.zoomable()){
30180 this.scale = this.startScale;
30188 onTouchEnd : function(e)
30190 this.pinching = false;
30191 this.dragable = false;
30195 process : function(file, crop)
30198 this.maskEl.mask(this.loadingText);
30201 this.xhr = new XMLHttpRequest();
30203 file.xhr = this.xhr;
30205 this.xhr.open(this.method, this.url, true);
30208 "Accept": "application/json",
30209 "Cache-Control": "no-cache",
30210 "X-Requested-With": "XMLHttpRequest"
30213 for (var headerName in headers) {
30214 var headerValue = headers[headerName];
30216 this.xhr.setRequestHeader(headerName, headerValue);
30222 this.xhr.onload = function()
30224 _this.xhrOnLoad(_this.xhr);
30227 this.xhr.onerror = function()
30229 _this.xhrOnError(_this.xhr);
30232 var formData = new FormData();
30234 formData.append('returnHTML', 'NO');
30237 formData.append('crop', crop);
30240 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30241 formData.append(this.paramName, file, file.name);
30244 if(typeof(file.filename) != 'undefined'){
30245 formData.append('filename', file.filename);
30248 if(typeof(file.mimetype) != 'undefined'){
30249 formData.append('mimetype', file.mimetype);
30252 if(this.fireEvent('arrange', this, formData) != false){
30253 this.xhr.send(formData);
30257 xhrOnLoad : function(xhr)
30260 this.maskEl.unmask();
30263 if (xhr.readyState !== 4) {
30264 this.fireEvent('exception', this, xhr);
30268 var response = Roo.decode(xhr.responseText);
30270 if(!response.success){
30271 this.fireEvent('exception', this, xhr);
30275 var response = Roo.decode(xhr.responseText);
30277 this.fireEvent('upload', this, response);
30281 xhrOnError : function()
30284 this.maskEl.unmask();
30287 Roo.log('xhr on error');
30289 var response = Roo.decode(xhr.responseText);
30295 prepare : function(file)
30298 this.maskEl.mask(this.loadingText);
30304 if(typeof(file) === 'string'){
30305 this.loadCanvas(file);
30309 if(!file || !this.urlAPI){
30314 this.cropType = file.type;
30318 if(this.fireEvent('prepare', this, this.file) != false){
30320 var reader = new FileReader();
30322 reader.onload = function (e) {
30323 if (e.target.error) {
30324 Roo.log(e.target.error);
30328 var buffer = e.target.result,
30329 dataView = new DataView(buffer),
30331 maxOffset = dataView.byteLength - 4,
30335 if (dataView.getUint16(0) === 0xffd8) {
30336 while (offset < maxOffset) {
30337 markerBytes = dataView.getUint16(offset);
30339 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30340 markerLength = dataView.getUint16(offset + 2) + 2;
30341 if (offset + markerLength > dataView.byteLength) {
30342 Roo.log('Invalid meta data: Invalid segment size.');
30346 if(markerBytes == 0xffe1){
30347 _this.parseExifData(
30354 offset += markerLength;
30364 var url = _this.urlAPI.createObjectURL(_this.file);
30366 _this.loadCanvas(url);
30371 reader.readAsArrayBuffer(this.file);
30377 parseExifData : function(dataView, offset, length)
30379 var tiffOffset = offset + 10,
30383 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30384 // No Exif data, might be XMP data instead
30388 // Check for the ASCII code for "Exif" (0x45786966):
30389 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30390 // No Exif data, might be XMP data instead
30393 if (tiffOffset + 8 > dataView.byteLength) {
30394 Roo.log('Invalid Exif data: Invalid segment size.');
30397 // Check for the two null bytes:
30398 if (dataView.getUint16(offset + 8) !== 0x0000) {
30399 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30402 // Check the byte alignment:
30403 switch (dataView.getUint16(tiffOffset)) {
30405 littleEndian = true;
30408 littleEndian = false;
30411 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30414 // Check for the TIFF tag marker (0x002A):
30415 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30416 Roo.log('Invalid Exif data: Missing TIFF marker.');
30419 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30420 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30422 this.parseExifTags(
30425 tiffOffset + dirOffset,
30430 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30435 if (dirOffset + 6 > dataView.byteLength) {
30436 Roo.log('Invalid Exif data: Invalid directory offset.');
30439 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30440 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30441 if (dirEndOffset + 4 > dataView.byteLength) {
30442 Roo.log('Invalid Exif data: Invalid directory size.');
30445 for (i = 0; i < tagsNumber; i += 1) {
30449 dirOffset + 2 + 12 * i, // tag offset
30453 // Return the offset to the next directory:
30454 return dataView.getUint32(dirEndOffset, littleEndian);
30457 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30459 var tag = dataView.getUint16(offset, littleEndian);
30461 this.exif[tag] = this.getExifValue(
30465 dataView.getUint16(offset + 2, littleEndian), // tag type
30466 dataView.getUint32(offset + 4, littleEndian), // tag length
30471 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30473 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30482 Roo.log('Invalid Exif data: Invalid tag type.');
30486 tagSize = tagType.size * length;
30487 // Determine if the value is contained in the dataOffset bytes,
30488 // or if the value at the dataOffset is a pointer to the actual data:
30489 dataOffset = tagSize > 4 ?
30490 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30491 if (dataOffset + tagSize > dataView.byteLength) {
30492 Roo.log('Invalid Exif data: Invalid data offset.');
30495 if (length === 1) {
30496 return tagType.getValue(dataView, dataOffset, littleEndian);
30499 for (i = 0; i < length; i += 1) {
30500 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30503 if (tagType.ascii) {
30505 // Concatenate the chars:
30506 for (i = 0; i < values.length; i += 1) {
30508 // Ignore the terminating NULL byte(s):
30509 if (c === '\u0000') {
30521 Roo.apply(Roo.bootstrap.UploadCropbox, {
30523 'Orientation': 0x0112
30527 1: 0, //'top-left',
30529 3: 180, //'bottom-right',
30530 // 4: 'bottom-left',
30532 6: 90, //'right-top',
30533 // 7: 'right-bottom',
30534 8: 270 //'left-bottom'
30538 // byte, 8-bit unsigned int:
30540 getValue: function (dataView, dataOffset) {
30541 return dataView.getUint8(dataOffset);
30545 // ascii, 8-bit byte:
30547 getValue: function (dataView, dataOffset) {
30548 return String.fromCharCode(dataView.getUint8(dataOffset));
30553 // short, 16 bit int:
30555 getValue: function (dataView, dataOffset, littleEndian) {
30556 return dataView.getUint16(dataOffset, littleEndian);
30560 // long, 32 bit int:
30562 getValue: function (dataView, dataOffset, littleEndian) {
30563 return dataView.getUint32(dataOffset, littleEndian);
30567 // rational = two long values, first is numerator, second is denominator:
30569 getValue: function (dataView, dataOffset, littleEndian) {
30570 return dataView.getUint32(dataOffset, littleEndian) /
30571 dataView.getUint32(dataOffset + 4, littleEndian);
30575 // slong, 32 bit signed int:
30577 getValue: function (dataView, dataOffset, littleEndian) {
30578 return dataView.getInt32(dataOffset, littleEndian);
30582 // srational, two slongs, first is numerator, second is denominator:
30584 getValue: function (dataView, dataOffset, littleEndian) {
30585 return dataView.getInt32(dataOffset, littleEndian) /
30586 dataView.getInt32(dataOffset + 4, littleEndian);
30596 cls : 'btn-group roo-upload-cropbox-rotate-left',
30597 action : 'rotate-left',
30601 cls : 'btn btn-default',
30602 html : '<i class="fa fa-undo"></i>'
30608 cls : 'btn-group roo-upload-cropbox-picture',
30609 action : 'picture',
30613 cls : 'btn btn-default',
30614 html : '<i class="fa fa-picture-o"></i>'
30620 cls : 'btn-group roo-upload-cropbox-rotate-right',
30621 action : 'rotate-right',
30625 cls : 'btn btn-default',
30626 html : '<i class="fa fa-repeat"></i>'
30634 cls : 'btn-group roo-upload-cropbox-rotate-left',
30635 action : 'rotate-left',
30639 cls : 'btn btn-default',
30640 html : '<i class="fa fa-undo"></i>'
30646 cls : 'btn-group roo-upload-cropbox-download',
30647 action : 'download',
30651 cls : 'btn btn-default',
30652 html : '<i class="fa fa-download"></i>'
30658 cls : 'btn-group roo-upload-cropbox-crop',
30663 cls : 'btn btn-default',
30664 html : '<i class="fa fa-crop"></i>'
30670 cls : 'btn-group roo-upload-cropbox-trash',
30675 cls : 'btn btn-default',
30676 html : '<i class="fa fa-trash"></i>'
30682 cls : 'btn-group roo-upload-cropbox-rotate-right',
30683 action : 'rotate-right',
30687 cls : 'btn btn-default',
30688 html : '<i class="fa fa-repeat"></i>'
30696 cls : 'btn-group roo-upload-cropbox-rotate-left',
30697 action : 'rotate-left',
30701 cls : 'btn btn-default',
30702 html : '<i class="fa fa-undo"></i>'
30708 cls : 'btn-group roo-upload-cropbox-rotate-right',
30709 action : 'rotate-right',
30713 cls : 'btn btn-default',
30714 html : '<i class="fa fa-repeat"></i>'
30727 * @class Roo.bootstrap.DocumentManager
30728 * @extends Roo.bootstrap.Component
30729 * Bootstrap DocumentManager class
30730 * @cfg {String} paramName default 'imageUpload'
30731 * @cfg {String} toolTipName default 'filename'
30732 * @cfg {String} method default POST
30733 * @cfg {String} url action url
30734 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30735 * @cfg {Boolean} multiple multiple upload default true
30736 * @cfg {Number} thumbSize default 300
30737 * @cfg {String} fieldLabel
30738 * @cfg {Number} labelWidth default 4
30739 * @cfg {String} labelAlign (left|top) default left
30740 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30741 * @cfg {Number} labellg set the width of label (1-12)
30742 * @cfg {Number} labelmd set the width of label (1-12)
30743 * @cfg {Number} labelsm set the width of label (1-12)
30744 * @cfg {Number} labelxs set the width of label (1-12)
30747 * Create a new DocumentManager
30748 * @param {Object} config The config object
30751 Roo.bootstrap.DocumentManager = function(config){
30752 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30755 this.delegates = [];
30760 * Fire when initial the DocumentManager
30761 * @param {Roo.bootstrap.DocumentManager} this
30766 * inspect selected file
30767 * @param {Roo.bootstrap.DocumentManager} this
30768 * @param {File} file
30773 * Fire when xhr load exception
30774 * @param {Roo.bootstrap.DocumentManager} this
30775 * @param {XMLHttpRequest} xhr
30777 "exception" : true,
30779 * @event afterupload
30780 * Fire when xhr load exception
30781 * @param {Roo.bootstrap.DocumentManager} this
30782 * @param {XMLHttpRequest} xhr
30784 "afterupload" : true,
30787 * prepare the form data
30788 * @param {Roo.bootstrap.DocumentManager} this
30789 * @param {Object} formData
30794 * Fire when remove the file
30795 * @param {Roo.bootstrap.DocumentManager} this
30796 * @param {Object} file
30801 * Fire after refresh the file
30802 * @param {Roo.bootstrap.DocumentManager} this
30807 * Fire after click the image
30808 * @param {Roo.bootstrap.DocumentManager} this
30809 * @param {Object} file
30814 * Fire when upload a image and editable set to true
30815 * @param {Roo.bootstrap.DocumentManager} this
30816 * @param {Object} file
30820 * @event beforeselectfile
30821 * Fire before select file
30822 * @param {Roo.bootstrap.DocumentManager} this
30824 "beforeselectfile" : true,
30827 * Fire before process file
30828 * @param {Roo.bootstrap.DocumentManager} this
30829 * @param {Object} file
30833 * @event previewrendered
30834 * Fire when preview rendered
30835 * @param {Roo.bootstrap.DocumentManager} this
30836 * @param {Object} file
30838 "previewrendered" : true,
30841 "previewResize" : true
30846 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30855 paramName : 'imageUpload',
30856 toolTipName : 'filename',
30859 labelAlign : 'left',
30869 getAutoCreate : function()
30871 var managerWidget = {
30873 cls : 'roo-document-manager',
30877 cls : 'roo-document-manager-selector',
30882 cls : 'roo-document-manager-uploader',
30886 cls : 'roo-document-manager-upload-btn',
30887 html : '<i class="fa fa-plus"></i>'
30898 cls : 'column col-md-12',
30903 if(this.fieldLabel.length){
30908 cls : 'column col-md-12',
30909 html : this.fieldLabel
30913 cls : 'column col-md-12',
30918 if(this.labelAlign == 'left'){
30923 html : this.fieldLabel
30932 if(this.labelWidth > 12){
30933 content[0].style = "width: " + this.labelWidth + 'px';
30936 if(this.labelWidth < 13 && this.labelmd == 0){
30937 this.labelmd = this.labelWidth;
30940 if(this.labellg > 0){
30941 content[0].cls += ' col-lg-' + this.labellg;
30942 content[1].cls += ' col-lg-' + (12 - this.labellg);
30945 if(this.labelmd > 0){
30946 content[0].cls += ' col-md-' + this.labelmd;
30947 content[1].cls += ' col-md-' + (12 - this.labelmd);
30950 if(this.labelsm > 0){
30951 content[0].cls += ' col-sm-' + this.labelsm;
30952 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30955 if(this.labelxs > 0){
30956 content[0].cls += ' col-xs-' + this.labelxs;
30957 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30965 cls : 'row clearfix',
30973 initEvents : function()
30975 this.managerEl = this.el.select('.roo-document-manager', true).first();
30976 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30978 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30979 this.selectorEl.hide();
30982 this.selectorEl.attr('multiple', 'multiple');
30985 this.selectorEl.on('change', this.onFileSelected, this);
30987 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30988 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30990 this.uploader.on('click', this.onUploaderClick, this);
30992 this.renderProgressDialog();
30996 window.addEventListener("resize", function() { _this.refresh(); } );
30998 this.fireEvent('initial', this);
31001 renderProgressDialog : function()
31005 this.progressDialog = new Roo.bootstrap.Modal({
31006 cls : 'roo-document-manager-progress-dialog',
31007 allow_close : false,
31018 btnclick : function() {
31019 _this.uploadCancel();
31025 this.progressDialog.render(Roo.get(document.body));
31027 this.progress = new Roo.bootstrap.Progress({
31028 cls : 'roo-document-manager-progress',
31033 this.progress.render(this.progressDialog.getChildContainer());
31035 this.progressBar = new Roo.bootstrap.ProgressBar({
31036 cls : 'roo-document-manager-progress-bar',
31039 aria_valuemax : 12,
31043 this.progressBar.render(this.progress.getChildContainer());
31046 onUploaderClick : function(e)
31048 e.preventDefault();
31050 if(this.fireEvent('beforeselectfile', this) != false){
31051 this.selectorEl.dom.click();
31056 onFileSelected : function(e)
31058 e.preventDefault();
31060 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31064 Roo.each(this.selectorEl.dom.files, function(file){
31065 if(this.fireEvent('inspect', this, file) != false){
31066 this.files.push(file);
31076 this.selectorEl.dom.value = '';
31078 if(!this.files || !this.files.length){
31082 if(this.boxes > 0 && this.files.length > this.boxes){
31083 this.files = this.files.slice(0, this.boxes);
31086 this.uploader.show();
31088 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31089 this.uploader.hide();
31098 Roo.each(this.files, function(file){
31100 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31101 var f = this.renderPreview(file);
31106 if(file.type.indexOf('image') != -1){
31107 this.delegates.push(
31109 _this.process(file);
31110 }).createDelegate(this)
31118 _this.process(file);
31119 }).createDelegate(this)
31124 this.files = files;
31126 this.delegates = this.delegates.concat(docs);
31128 if(!this.delegates.length){
31133 this.progressBar.aria_valuemax = this.delegates.length;
31140 arrange : function()
31142 if(!this.delegates.length){
31143 this.progressDialog.hide();
31148 var delegate = this.delegates.shift();
31150 this.progressDialog.show();
31152 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31154 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31159 refresh : function()
31161 this.uploader.show();
31163 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31164 this.uploader.hide();
31167 Roo.isTouch ? this.closable(false) : this.closable(true);
31169 this.fireEvent('refresh', this);
31172 onRemove : function(e, el, o)
31174 e.preventDefault();
31176 this.fireEvent('remove', this, o);
31180 remove : function(o)
31184 Roo.each(this.files, function(file){
31185 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31194 this.files = files;
31201 Roo.each(this.files, function(file){
31206 file.target.remove();
31215 onClick : function(e, el, o)
31217 e.preventDefault();
31219 this.fireEvent('click', this, o);
31223 closable : function(closable)
31225 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31227 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31239 xhrOnLoad : function(xhr)
31241 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31245 if (xhr.readyState !== 4) {
31247 this.fireEvent('exception', this, xhr);
31251 var response = Roo.decode(xhr.responseText);
31253 if(!response.success){
31255 this.fireEvent('exception', this, xhr);
31259 var file = this.renderPreview(response.data);
31261 this.files.push(file);
31265 this.fireEvent('afterupload', this, xhr);
31269 xhrOnError : function(xhr)
31271 Roo.log('xhr on error');
31273 var response = Roo.decode(xhr.responseText);
31280 process : function(file)
31282 if(this.fireEvent('process', this, file) !== false){
31283 if(this.editable && file.type.indexOf('image') != -1){
31284 this.fireEvent('edit', this, file);
31288 this.uploadStart(file, false);
31295 uploadStart : function(file, crop)
31297 this.xhr = new XMLHttpRequest();
31299 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31304 file.xhr = this.xhr;
31306 this.managerEl.createChild({
31308 cls : 'roo-document-manager-loading',
31312 tooltip : file.name,
31313 cls : 'roo-document-manager-thumb',
31314 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31320 this.xhr.open(this.method, this.url, true);
31323 "Accept": "application/json",
31324 "Cache-Control": "no-cache",
31325 "X-Requested-With": "XMLHttpRequest"
31328 for (var headerName in headers) {
31329 var headerValue = headers[headerName];
31331 this.xhr.setRequestHeader(headerName, headerValue);
31337 this.xhr.onload = function()
31339 _this.xhrOnLoad(_this.xhr);
31342 this.xhr.onerror = function()
31344 _this.xhrOnError(_this.xhr);
31347 var formData = new FormData();
31349 formData.append('returnHTML', 'NO');
31352 formData.append('crop', crop);
31355 formData.append(this.paramName, file, file.name);
31362 if(this.fireEvent('prepare', this, formData, options) != false){
31364 if(options.manually){
31368 this.xhr.send(formData);
31372 this.uploadCancel();
31375 uploadCancel : function()
31381 this.delegates = [];
31383 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31390 renderPreview : function(file)
31392 if(typeof(file.target) != 'undefined' && file.target){
31396 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31398 var previewEl = this.managerEl.createChild({
31400 cls : 'roo-document-manager-preview',
31404 tooltip : file[this.toolTipName],
31405 cls : 'roo-document-manager-thumb',
31406 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31411 html : '<i class="fa fa-times-circle"></i>'
31416 var close = previewEl.select('button.close', true).first();
31418 close.on('click', this.onRemove, this, file);
31420 file.target = previewEl;
31422 var image = previewEl.select('img', true).first();
31426 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31428 image.on('click', this.onClick, this, file);
31430 this.fireEvent('previewrendered', this, file);
31436 onPreviewLoad : function(file, image)
31438 if(typeof(file.target) == 'undefined' || !file.target){
31442 var width = image.dom.naturalWidth || image.dom.width;
31443 var height = image.dom.naturalHeight || image.dom.height;
31445 if(!this.previewResize) {
31449 if(width > height){
31450 file.target.addClass('wide');
31454 file.target.addClass('tall');
31459 uploadFromSource : function(file, crop)
31461 this.xhr = new XMLHttpRequest();
31463 this.managerEl.createChild({
31465 cls : 'roo-document-manager-loading',
31469 tooltip : file.name,
31470 cls : 'roo-document-manager-thumb',
31471 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31477 this.xhr.open(this.method, this.url, true);
31480 "Accept": "application/json",
31481 "Cache-Control": "no-cache",
31482 "X-Requested-With": "XMLHttpRequest"
31485 for (var headerName in headers) {
31486 var headerValue = headers[headerName];
31488 this.xhr.setRequestHeader(headerName, headerValue);
31494 this.xhr.onload = function()
31496 _this.xhrOnLoad(_this.xhr);
31499 this.xhr.onerror = function()
31501 _this.xhrOnError(_this.xhr);
31504 var formData = new FormData();
31506 formData.append('returnHTML', 'NO');
31508 formData.append('crop', crop);
31510 if(typeof(file.filename) != 'undefined'){
31511 formData.append('filename', file.filename);
31514 if(typeof(file.mimetype) != 'undefined'){
31515 formData.append('mimetype', file.mimetype);
31520 if(this.fireEvent('prepare', this, formData) != false){
31521 this.xhr.send(formData);
31531 * @class Roo.bootstrap.DocumentViewer
31532 * @extends Roo.bootstrap.Component
31533 * Bootstrap DocumentViewer class
31534 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31535 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31538 * Create a new DocumentViewer
31539 * @param {Object} config The config object
31542 Roo.bootstrap.DocumentViewer = function(config){
31543 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31548 * Fire after initEvent
31549 * @param {Roo.bootstrap.DocumentViewer} this
31555 * @param {Roo.bootstrap.DocumentViewer} this
31560 * Fire after download button
31561 * @param {Roo.bootstrap.DocumentViewer} this
31566 * Fire after trash button
31567 * @param {Roo.bootstrap.DocumentViewer} this
31574 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31576 showDownload : true,
31580 getAutoCreate : function()
31584 cls : 'roo-document-viewer',
31588 cls : 'roo-document-viewer-body',
31592 cls : 'roo-document-viewer-thumb',
31596 cls : 'roo-document-viewer-image'
31604 cls : 'roo-document-viewer-footer',
31607 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31611 cls : 'btn-group roo-document-viewer-download',
31615 cls : 'btn btn-default',
31616 html : '<i class="fa fa-download"></i>'
31622 cls : 'btn-group roo-document-viewer-trash',
31626 cls : 'btn btn-default',
31627 html : '<i class="fa fa-trash"></i>'
31640 initEvents : function()
31642 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31643 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31645 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31646 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31648 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31649 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31651 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31652 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31654 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31655 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31657 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31658 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31660 this.bodyEl.on('click', this.onClick, this);
31661 this.downloadBtn.on('click', this.onDownload, this);
31662 this.trashBtn.on('click', this.onTrash, this);
31664 this.downloadBtn.hide();
31665 this.trashBtn.hide();
31667 if(this.showDownload){
31668 this.downloadBtn.show();
31671 if(this.showTrash){
31672 this.trashBtn.show();
31675 if(!this.showDownload && !this.showTrash) {
31676 this.footerEl.hide();
31681 initial : function()
31683 this.fireEvent('initial', this);
31687 onClick : function(e)
31689 e.preventDefault();
31691 this.fireEvent('click', this);
31694 onDownload : function(e)
31696 e.preventDefault();
31698 this.fireEvent('download', this);
31701 onTrash : function(e)
31703 e.preventDefault();
31705 this.fireEvent('trash', this);
31717 * @class Roo.bootstrap.NavProgressBar
31718 * @extends Roo.bootstrap.Component
31719 * Bootstrap NavProgressBar class
31722 * Create a new nav progress bar
31723 * @param {Object} config The config object
31726 Roo.bootstrap.NavProgressBar = function(config){
31727 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31729 this.bullets = this.bullets || [];
31731 // Roo.bootstrap.NavProgressBar.register(this);
31735 * Fires when the active item changes
31736 * @param {Roo.bootstrap.NavProgressBar} this
31737 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31738 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31745 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31750 getAutoCreate : function()
31752 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31756 cls : 'roo-navigation-bar-group',
31760 cls : 'roo-navigation-top-bar'
31764 cls : 'roo-navigation-bullets-bar',
31768 cls : 'roo-navigation-bar'
31775 cls : 'roo-navigation-bottom-bar'
31785 initEvents: function()
31790 onRender : function(ct, position)
31792 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31794 if(this.bullets.length){
31795 Roo.each(this.bullets, function(b){
31804 addItem : function(cfg)
31806 var item = new Roo.bootstrap.NavProgressItem(cfg);
31808 item.parentId = this.id;
31809 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31812 var top = new Roo.bootstrap.Element({
31814 cls : 'roo-navigation-bar-text'
31817 var bottom = new Roo.bootstrap.Element({
31819 cls : 'roo-navigation-bar-text'
31822 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31823 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31825 var topText = new Roo.bootstrap.Element({
31827 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31830 var bottomText = new Roo.bootstrap.Element({
31832 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31835 topText.onRender(top.el, null);
31836 bottomText.onRender(bottom.el, null);
31839 item.bottomEl = bottom;
31842 this.barItems.push(item);
31847 getActive : function()
31849 var active = false;
31851 Roo.each(this.barItems, function(v){
31853 if (!v.isActive()) {
31865 setActiveItem : function(item)
31869 Roo.each(this.barItems, function(v){
31870 if (v.rid == item.rid) {
31874 if (v.isActive()) {
31875 v.setActive(false);
31880 item.setActive(true);
31882 this.fireEvent('changed', this, item, prev);
31885 getBarItem: function(rid)
31889 Roo.each(this.barItems, function(e) {
31890 if (e.rid != rid) {
31901 indexOfItem : function(item)
31905 Roo.each(this.barItems, function(v, i){
31907 if (v.rid != item.rid) {
31918 setActiveNext : function()
31920 var i = this.indexOfItem(this.getActive());
31922 if (i > this.barItems.length) {
31926 this.setActiveItem(this.barItems[i+1]);
31929 setActivePrev : function()
31931 var i = this.indexOfItem(this.getActive());
31937 this.setActiveItem(this.barItems[i-1]);
31940 format : function()
31942 if(!this.barItems.length){
31946 var width = 100 / this.barItems.length;
31948 Roo.each(this.barItems, function(i){
31949 i.el.setStyle('width', width + '%');
31950 i.topEl.el.setStyle('width', width + '%');
31951 i.bottomEl.el.setStyle('width', width + '%');
31960 * Nav Progress Item
31965 * @class Roo.bootstrap.NavProgressItem
31966 * @extends Roo.bootstrap.Component
31967 * Bootstrap NavProgressItem class
31968 * @cfg {String} rid the reference id
31969 * @cfg {Boolean} active (true|false) Is item active default false
31970 * @cfg {Boolean} disabled (true|false) Is item active default false
31971 * @cfg {String} html
31972 * @cfg {String} position (top|bottom) text position default bottom
31973 * @cfg {String} icon show icon instead of number
31976 * Create a new NavProgressItem
31977 * @param {Object} config The config object
31979 Roo.bootstrap.NavProgressItem = function(config){
31980 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31985 * The raw click event for the entire grid.
31986 * @param {Roo.bootstrap.NavProgressItem} this
31987 * @param {Roo.EventObject} e
31994 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32000 position : 'bottom',
32003 getAutoCreate : function()
32005 var iconCls = 'roo-navigation-bar-item-icon';
32007 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32011 cls: 'roo-navigation-bar-item',
32021 cfg.cls += ' active';
32024 cfg.cls += ' disabled';
32030 disable : function()
32032 this.setDisabled(true);
32035 enable : function()
32037 this.setDisabled(false);
32040 initEvents: function()
32042 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32044 this.iconEl.on('click', this.onClick, this);
32047 onClick : function(e)
32049 e.preventDefault();
32055 if(this.fireEvent('click', this, e) === false){
32059 this.parent().setActiveItem(this);
32062 isActive: function ()
32064 return this.active;
32067 setActive : function(state)
32069 if(this.active == state){
32073 this.active = state;
32076 this.el.addClass('active');
32080 this.el.removeClass('active');
32085 setDisabled : function(state)
32087 if(this.disabled == state){
32091 this.disabled = state;
32094 this.el.addClass('disabled');
32098 this.el.removeClass('disabled');
32101 tooltipEl : function()
32103 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32116 * @class Roo.bootstrap.FieldLabel
32117 * @extends Roo.bootstrap.Component
32118 * Bootstrap FieldLabel class
32119 * @cfg {String} html contents of the element
32120 * @cfg {String} tag tag of the element default label
32121 * @cfg {String} cls class of the element
32122 * @cfg {String} target label target
32123 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32124 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32125 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32126 * @cfg {String} iconTooltip default "This field is required"
32127 * @cfg {String} indicatorpos (left|right) default left
32130 * Create a new FieldLabel
32131 * @param {Object} config The config object
32134 Roo.bootstrap.FieldLabel = function(config){
32135 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32140 * Fires after the field has been marked as invalid.
32141 * @param {Roo.form.FieldLabel} this
32142 * @param {String} msg The validation message
32147 * Fires after the field has been validated with no errors.
32148 * @param {Roo.form.FieldLabel} this
32154 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32161 invalidClass : 'has-warning',
32162 validClass : 'has-success',
32163 iconTooltip : 'This field is required',
32164 indicatorpos : 'left',
32166 getAutoCreate : function(){
32169 if (!this.allowBlank) {
32175 cls : 'roo-bootstrap-field-label ' + this.cls,
32180 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32181 tooltip : this.iconTooltip
32190 if(this.indicatorpos == 'right'){
32193 cls : 'roo-bootstrap-field-label ' + this.cls,
32202 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32203 tooltip : this.iconTooltip
32212 initEvents: function()
32214 Roo.bootstrap.Element.superclass.initEvents.call(this);
32216 this.indicator = this.indicatorEl();
32218 if(this.indicator){
32219 this.indicator.removeClass('visible');
32220 this.indicator.addClass('invisible');
32223 Roo.bootstrap.FieldLabel.register(this);
32226 indicatorEl : function()
32228 var indicator = this.el.select('i.roo-required-indicator',true).first();
32239 * Mark this field as valid
32241 markValid : function()
32243 if(this.indicator){
32244 this.indicator.removeClass('visible');
32245 this.indicator.addClass('invisible');
32247 if (Roo.bootstrap.version == 3) {
32248 this.el.removeClass(this.invalidClass);
32249 this.el.addClass(this.validClass);
32251 this.el.removeClass('is-invalid');
32252 this.el.addClass('is-valid');
32256 this.fireEvent('valid', this);
32260 * Mark this field as invalid
32261 * @param {String} msg The validation message
32263 markInvalid : function(msg)
32265 if(this.indicator){
32266 this.indicator.removeClass('invisible');
32267 this.indicator.addClass('visible');
32269 if (Roo.bootstrap.version == 3) {
32270 this.el.removeClass(this.validClass);
32271 this.el.addClass(this.invalidClass);
32273 this.el.removeClass('is-valid');
32274 this.el.addClass('is-invalid');
32278 this.fireEvent('invalid', this, msg);
32284 Roo.apply(Roo.bootstrap.FieldLabel, {
32289 * register a FieldLabel Group
32290 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32292 register : function(label)
32294 if(this.groups.hasOwnProperty(label.target)){
32298 this.groups[label.target] = label;
32302 * fetch a FieldLabel Group based on the target
32303 * @param {string} target
32304 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32306 get: function(target) {
32307 if (typeof(this.groups[target]) == 'undefined') {
32311 return this.groups[target] ;
32320 * page DateSplitField.
32326 * @class Roo.bootstrap.DateSplitField
32327 * @extends Roo.bootstrap.Component
32328 * Bootstrap DateSplitField class
32329 * @cfg {string} fieldLabel - the label associated
32330 * @cfg {Number} labelWidth set the width of label (0-12)
32331 * @cfg {String} labelAlign (top|left)
32332 * @cfg {Boolean} dayAllowBlank (true|false) default false
32333 * @cfg {Boolean} monthAllowBlank (true|false) default false
32334 * @cfg {Boolean} yearAllowBlank (true|false) default false
32335 * @cfg {string} dayPlaceholder
32336 * @cfg {string} monthPlaceholder
32337 * @cfg {string} yearPlaceholder
32338 * @cfg {string} dayFormat default 'd'
32339 * @cfg {string} monthFormat default 'm'
32340 * @cfg {string} yearFormat default 'Y'
32341 * @cfg {Number} labellg set the width of label (1-12)
32342 * @cfg {Number} labelmd set the width of label (1-12)
32343 * @cfg {Number} labelsm set the width of label (1-12)
32344 * @cfg {Number} labelxs set the width of label (1-12)
32348 * Create a new DateSplitField
32349 * @param {Object} config The config object
32352 Roo.bootstrap.DateSplitField = function(config){
32353 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32359 * getting the data of years
32360 * @param {Roo.bootstrap.DateSplitField} this
32361 * @param {Object} years
32366 * getting the data of days
32367 * @param {Roo.bootstrap.DateSplitField} this
32368 * @param {Object} days
32373 * Fires after the field has been marked as invalid.
32374 * @param {Roo.form.Field} this
32375 * @param {String} msg The validation message
32380 * Fires after the field has been validated with no errors.
32381 * @param {Roo.form.Field} this
32387 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32390 labelAlign : 'top',
32392 dayAllowBlank : false,
32393 monthAllowBlank : false,
32394 yearAllowBlank : false,
32395 dayPlaceholder : '',
32396 monthPlaceholder : '',
32397 yearPlaceholder : '',
32401 isFormField : true,
32407 getAutoCreate : function()
32411 cls : 'row roo-date-split-field-group',
32416 cls : 'form-hidden-field roo-date-split-field-group-value',
32422 var labelCls = 'col-md-12';
32423 var contentCls = 'col-md-4';
32425 if(this.fieldLabel){
32429 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32433 html : this.fieldLabel
32438 if(this.labelAlign == 'left'){
32440 if(this.labelWidth > 12){
32441 label.style = "width: " + this.labelWidth + 'px';
32444 if(this.labelWidth < 13 && this.labelmd == 0){
32445 this.labelmd = this.labelWidth;
32448 if(this.labellg > 0){
32449 labelCls = ' col-lg-' + this.labellg;
32450 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32453 if(this.labelmd > 0){
32454 labelCls = ' col-md-' + this.labelmd;
32455 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32458 if(this.labelsm > 0){
32459 labelCls = ' col-sm-' + this.labelsm;
32460 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32463 if(this.labelxs > 0){
32464 labelCls = ' col-xs-' + this.labelxs;
32465 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32469 label.cls += ' ' + labelCls;
32471 cfg.cn.push(label);
32474 Roo.each(['day', 'month', 'year'], function(t){
32477 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32484 inputEl: function ()
32486 return this.el.select('.roo-date-split-field-group-value', true).first();
32489 onRender : function(ct, position)
32493 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32495 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32497 this.dayField = new Roo.bootstrap.ComboBox({
32498 allowBlank : this.dayAllowBlank,
32499 alwaysQuery : true,
32500 displayField : 'value',
32503 forceSelection : true,
32505 placeholder : this.dayPlaceholder,
32506 selectOnFocus : true,
32507 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32508 triggerAction : 'all',
32510 valueField : 'value',
32511 store : new Roo.data.SimpleStore({
32512 data : (function() {
32514 _this.fireEvent('days', _this, days);
32517 fields : [ 'value' ]
32520 select : function (_self, record, index)
32522 _this.setValue(_this.getValue());
32527 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32529 this.monthField = new Roo.bootstrap.MonthField({
32530 after : '<i class=\"fa fa-calendar\"></i>',
32531 allowBlank : this.monthAllowBlank,
32532 placeholder : this.monthPlaceholder,
32535 render : function (_self)
32537 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32538 e.preventDefault();
32542 select : function (_self, oldvalue, newvalue)
32544 _this.setValue(_this.getValue());
32549 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32551 this.yearField = new Roo.bootstrap.ComboBox({
32552 allowBlank : this.yearAllowBlank,
32553 alwaysQuery : true,
32554 displayField : 'value',
32557 forceSelection : true,
32559 placeholder : this.yearPlaceholder,
32560 selectOnFocus : true,
32561 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32562 triggerAction : 'all',
32564 valueField : 'value',
32565 store : new Roo.data.SimpleStore({
32566 data : (function() {
32568 _this.fireEvent('years', _this, years);
32571 fields : [ 'value' ]
32574 select : function (_self, record, index)
32576 _this.setValue(_this.getValue());
32581 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32584 setValue : function(v, format)
32586 this.inputEl.dom.value = v;
32588 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32590 var d = Date.parseDate(v, f);
32597 this.setDay(d.format(this.dayFormat));
32598 this.setMonth(d.format(this.monthFormat));
32599 this.setYear(d.format(this.yearFormat));
32606 setDay : function(v)
32608 this.dayField.setValue(v);
32609 this.inputEl.dom.value = this.getValue();
32614 setMonth : function(v)
32616 this.monthField.setValue(v, true);
32617 this.inputEl.dom.value = this.getValue();
32622 setYear : function(v)
32624 this.yearField.setValue(v);
32625 this.inputEl.dom.value = this.getValue();
32630 getDay : function()
32632 return this.dayField.getValue();
32635 getMonth : function()
32637 return this.monthField.getValue();
32640 getYear : function()
32642 return this.yearField.getValue();
32645 getValue : function()
32647 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32649 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32659 this.inputEl.dom.value = '';
32664 validate : function()
32666 var d = this.dayField.validate();
32667 var m = this.monthField.validate();
32668 var y = this.yearField.validate();
32673 (!this.dayAllowBlank && !d) ||
32674 (!this.monthAllowBlank && !m) ||
32675 (!this.yearAllowBlank && !y)
32680 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32689 this.markInvalid();
32694 markValid : function()
32697 var label = this.el.select('label', true).first();
32698 var icon = this.el.select('i.fa-star', true).first();
32704 this.fireEvent('valid', this);
32708 * Mark this field as invalid
32709 * @param {String} msg The validation message
32711 markInvalid : function(msg)
32714 var label = this.el.select('label', true).first();
32715 var icon = this.el.select('i.fa-star', true).first();
32717 if(label && !icon){
32718 this.el.select('.roo-date-split-field-label', true).createChild({
32720 cls : 'text-danger fa fa-lg fa-star',
32721 tooltip : 'This field is required',
32722 style : 'margin-right:5px;'
32726 this.fireEvent('invalid', this, msg);
32729 clearInvalid : function()
32731 var label = this.el.select('label', true).first();
32732 var icon = this.el.select('i.fa-star', true).first();
32738 this.fireEvent('valid', this);
32741 getName: function()
32751 * http://masonry.desandro.com
32753 * The idea is to render all the bricks based on vertical width...
32755 * The original code extends 'outlayer' - we might need to use that....
32761 * @class Roo.bootstrap.LayoutMasonry
32762 * @extends Roo.bootstrap.Component
32763 * Bootstrap Layout Masonry class
32766 * Create a new Element
32767 * @param {Object} config The config object
32770 Roo.bootstrap.LayoutMasonry = function(config){
32772 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32776 Roo.bootstrap.LayoutMasonry.register(this);
32782 * Fire after layout the items
32783 * @param {Roo.bootstrap.LayoutMasonry} this
32784 * @param {Roo.EventObject} e
32791 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32794 * @cfg {Boolean} isLayoutInstant = no animation?
32796 isLayoutInstant : false, // needed?
32799 * @cfg {Number} boxWidth width of the columns
32804 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32809 * @cfg {Number} padWidth padding below box..
32814 * @cfg {Number} gutter gutter width..
32819 * @cfg {Number} maxCols maximum number of columns
32825 * @cfg {Boolean} isAutoInitial defalut true
32827 isAutoInitial : true,
32832 * @cfg {Boolean} isHorizontal defalut false
32834 isHorizontal : false,
32836 currentSize : null,
32842 bricks: null, //CompositeElement
32846 _isLayoutInited : false,
32848 // isAlternative : false, // only use for vertical layout...
32851 * @cfg {Number} alternativePadWidth padding below box..
32853 alternativePadWidth : 50,
32855 selectedBrick : [],
32857 getAutoCreate : function(){
32859 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32863 cls: 'blog-masonary-wrapper ' + this.cls,
32865 cls : 'mas-boxes masonary'
32872 getChildContainer: function( )
32874 if (this.boxesEl) {
32875 return this.boxesEl;
32878 this.boxesEl = this.el.select('.mas-boxes').first();
32880 return this.boxesEl;
32884 initEvents : function()
32888 if(this.isAutoInitial){
32889 Roo.log('hook children rendered');
32890 this.on('childrenrendered', function() {
32891 Roo.log('children rendered');
32897 initial : function()
32899 this.selectedBrick = [];
32901 this.currentSize = this.el.getBox(true);
32903 Roo.EventManager.onWindowResize(this.resize, this);
32905 if(!this.isAutoInitial){
32913 //this.layout.defer(500,this);
32917 resize : function()
32919 var cs = this.el.getBox(true);
32922 this.currentSize.width == cs.width &&
32923 this.currentSize.x == cs.x &&
32924 this.currentSize.height == cs.height &&
32925 this.currentSize.y == cs.y
32927 Roo.log("no change in with or X or Y");
32931 this.currentSize = cs;
32937 layout : function()
32939 this._resetLayout();
32941 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32943 this.layoutItems( isInstant );
32945 this._isLayoutInited = true;
32947 this.fireEvent('layout', this);
32951 _resetLayout : function()
32953 if(this.isHorizontal){
32954 this.horizontalMeasureColumns();
32958 this.verticalMeasureColumns();
32962 verticalMeasureColumns : function()
32964 this.getContainerWidth();
32966 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32967 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32971 var boxWidth = this.boxWidth + this.padWidth;
32973 if(this.containerWidth < this.boxWidth){
32974 boxWidth = this.containerWidth
32977 var containerWidth = this.containerWidth;
32979 var cols = Math.floor(containerWidth / boxWidth);
32981 this.cols = Math.max( cols, 1 );
32983 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32985 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32987 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32989 this.colWidth = boxWidth + avail - this.padWidth;
32991 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32992 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32995 horizontalMeasureColumns : function()
32997 this.getContainerWidth();
32999 var boxWidth = this.boxWidth;
33001 if(this.containerWidth < boxWidth){
33002 boxWidth = this.containerWidth;
33005 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33007 this.el.setHeight(boxWidth);
33011 getContainerWidth : function()
33013 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33016 layoutItems : function( isInstant )
33018 Roo.log(this.bricks);
33020 var items = Roo.apply([], this.bricks);
33022 if(this.isHorizontal){
33023 this._horizontalLayoutItems( items , isInstant );
33027 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33028 // this._verticalAlternativeLayoutItems( items , isInstant );
33032 this._verticalLayoutItems( items , isInstant );
33036 _verticalLayoutItems : function ( items , isInstant)
33038 if ( !items || !items.length ) {
33043 ['xs', 'xs', 'xs', 'tall'],
33044 ['xs', 'xs', 'tall'],
33045 ['xs', 'xs', 'sm'],
33046 ['xs', 'xs', 'xs'],
33052 ['sm', 'xs', 'xs'],
33056 ['tall', 'xs', 'xs', 'xs'],
33057 ['tall', 'xs', 'xs'],
33069 Roo.each(items, function(item, k){
33071 switch (item.size) {
33072 // these layouts take up a full box,
33083 boxes.push([item]);
33106 var filterPattern = function(box, length)
33114 var pattern = box.slice(0, length);
33118 Roo.each(pattern, function(i){
33119 format.push(i.size);
33122 Roo.each(standard, function(s){
33124 if(String(s) != String(format)){
33133 if(!match && length == 1){
33138 filterPattern(box, length - 1);
33142 queue.push(pattern);
33144 box = box.slice(length, box.length);
33146 filterPattern(box, 4);
33152 Roo.each(boxes, function(box, k){
33158 if(box.length == 1){
33163 filterPattern(box, 4);
33167 this._processVerticalLayoutQueue( queue, isInstant );
33171 // _verticalAlternativeLayoutItems : function( items , isInstant )
33173 // if ( !items || !items.length ) {
33177 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33181 _horizontalLayoutItems : function ( items , isInstant)
33183 if ( !items || !items.length || items.length < 3) {
33189 var eItems = items.slice(0, 3);
33191 items = items.slice(3, items.length);
33194 ['xs', 'xs', 'xs', 'wide'],
33195 ['xs', 'xs', 'wide'],
33196 ['xs', 'xs', 'sm'],
33197 ['xs', 'xs', 'xs'],
33203 ['sm', 'xs', 'xs'],
33207 ['wide', 'xs', 'xs', 'xs'],
33208 ['wide', 'xs', 'xs'],
33221 Roo.each(items, function(item, k){
33223 switch (item.size) {
33234 boxes.push([item]);
33258 var filterPattern = function(box, length)
33266 var pattern = box.slice(0, length);
33270 Roo.each(pattern, function(i){
33271 format.push(i.size);
33274 Roo.each(standard, function(s){
33276 if(String(s) != String(format)){
33285 if(!match && length == 1){
33290 filterPattern(box, length - 1);
33294 queue.push(pattern);
33296 box = box.slice(length, box.length);
33298 filterPattern(box, 4);
33304 Roo.each(boxes, function(box, k){
33310 if(box.length == 1){
33315 filterPattern(box, 4);
33322 var pos = this.el.getBox(true);
33326 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33328 var hit_end = false;
33330 Roo.each(queue, function(box){
33334 Roo.each(box, function(b){
33336 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33346 Roo.each(box, function(b){
33348 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33351 mx = Math.max(mx, b.x);
33355 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33359 Roo.each(box, function(b){
33361 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33375 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33378 /** Sets position of item in DOM
33379 * @param {Element} item
33380 * @param {Number} x - horizontal position
33381 * @param {Number} y - vertical position
33382 * @param {Boolean} isInstant - disables transitions
33384 _processVerticalLayoutQueue : function( queue, isInstant )
33386 var pos = this.el.getBox(true);
33391 for (var i = 0; i < this.cols; i++){
33395 Roo.each(queue, function(box, k){
33397 var col = k % this.cols;
33399 Roo.each(box, function(b,kk){
33401 b.el.position('absolute');
33403 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33404 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33406 if(b.size == 'md-left' || b.size == 'md-right'){
33407 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33408 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33411 b.el.setWidth(width);
33412 b.el.setHeight(height);
33414 b.el.select('iframe',true).setSize(width,height);
33418 for (var i = 0; i < this.cols; i++){
33420 if(maxY[i] < maxY[col]){
33425 col = Math.min(col, i);
33429 x = pos.x + col * (this.colWidth + this.padWidth);
33433 var positions = [];
33435 switch (box.length){
33437 positions = this.getVerticalOneBoxColPositions(x, y, box);
33440 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33443 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33446 positions = this.getVerticalFourBoxColPositions(x, y, box);
33452 Roo.each(box, function(b,kk){
33454 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33456 var sz = b.el.getSize();
33458 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33466 for (var i = 0; i < this.cols; i++){
33467 mY = Math.max(mY, maxY[i]);
33470 this.el.setHeight(mY - pos.y);
33474 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33476 // var pos = this.el.getBox(true);
33479 // var maxX = pos.right;
33481 // var maxHeight = 0;
33483 // Roo.each(items, function(item, k){
33487 // item.el.position('absolute');
33489 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33491 // item.el.setWidth(width);
33493 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33495 // item.el.setHeight(height);
33498 // item.el.setXY([x, y], isInstant ? false : true);
33500 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33503 // y = y + height + this.alternativePadWidth;
33505 // maxHeight = maxHeight + height + this.alternativePadWidth;
33509 // this.el.setHeight(maxHeight);
33513 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33515 var pos = this.el.getBox(true);
33520 var maxX = pos.right;
33522 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33524 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33526 Roo.each(queue, function(box, k){
33528 Roo.each(box, function(b, kk){
33530 b.el.position('absolute');
33532 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33533 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33535 if(b.size == 'md-left' || b.size == 'md-right'){
33536 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33537 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33540 b.el.setWidth(width);
33541 b.el.setHeight(height);
33549 var positions = [];
33551 switch (box.length){
33553 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33556 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33559 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33562 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33568 Roo.each(box, function(b,kk){
33570 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33572 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33580 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33582 Roo.each(eItems, function(b,k){
33584 b.size = (k == 0) ? 'sm' : 'xs';
33585 b.x = (k == 0) ? 2 : 1;
33586 b.y = (k == 0) ? 2 : 1;
33588 b.el.position('absolute');
33590 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33592 b.el.setWidth(width);
33594 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33596 b.el.setHeight(height);
33600 var positions = [];
33603 x : maxX - this.unitWidth * 2 - this.gutter,
33608 x : maxX - this.unitWidth,
33609 y : minY + (this.unitWidth + this.gutter) * 2
33613 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33617 Roo.each(eItems, function(b,k){
33619 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33625 getVerticalOneBoxColPositions : function(x, y, box)
33629 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33631 if(box[0].size == 'md-left'){
33635 if(box[0].size == 'md-right'){
33640 x : x + (this.unitWidth + this.gutter) * rand,
33647 getVerticalTwoBoxColPositions : function(x, y, box)
33651 if(box[0].size == 'xs'){
33655 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33659 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33673 x : x + (this.unitWidth + this.gutter) * 2,
33674 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33681 getVerticalThreeBoxColPositions : function(x, y, box)
33685 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33693 x : x + (this.unitWidth + this.gutter) * 1,
33698 x : x + (this.unitWidth + this.gutter) * 2,
33706 if(box[0].size == 'xs' && box[1].size == 'xs'){
33715 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33719 x : x + (this.unitWidth + this.gutter) * 1,
33733 x : x + (this.unitWidth + this.gutter) * 2,
33738 x : x + (this.unitWidth + this.gutter) * 2,
33739 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33746 getVerticalFourBoxColPositions : function(x, y, box)
33750 if(box[0].size == 'xs'){
33759 y : y + (this.unitHeight + this.gutter) * 1
33764 y : y + (this.unitHeight + this.gutter) * 2
33768 x : x + (this.unitWidth + this.gutter) * 1,
33782 x : x + (this.unitWidth + this.gutter) * 2,
33787 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33788 y : y + (this.unitHeight + this.gutter) * 1
33792 x : x + (this.unitWidth + this.gutter) * 2,
33793 y : y + (this.unitWidth + this.gutter) * 2
33800 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33804 if(box[0].size == 'md-left'){
33806 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33813 if(box[0].size == 'md-right'){
33815 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33816 y : minY + (this.unitWidth + this.gutter) * 1
33822 var rand = Math.floor(Math.random() * (4 - box[0].y));
33825 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33826 y : minY + (this.unitWidth + this.gutter) * rand
33833 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33837 if(box[0].size == 'xs'){
33840 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33845 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33846 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33854 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33859 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33860 y : minY + (this.unitWidth + this.gutter) * 2
33867 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33871 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33874 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33879 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33880 y : minY + (this.unitWidth + this.gutter) * 1
33884 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33885 y : minY + (this.unitWidth + this.gutter) * 2
33892 if(box[0].size == 'xs' && box[1].size == 'xs'){
33895 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33900 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33905 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33906 y : minY + (this.unitWidth + this.gutter) * 1
33914 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33919 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33920 y : minY + (this.unitWidth + this.gutter) * 2
33924 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33925 y : minY + (this.unitWidth + this.gutter) * 2
33932 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33936 if(box[0].size == 'xs'){
33939 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33944 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33949 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),
33954 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33955 y : minY + (this.unitWidth + this.gutter) * 1
33963 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33968 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33969 y : minY + (this.unitWidth + this.gutter) * 2
33973 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33974 y : minY + (this.unitWidth + this.gutter) * 2
33978 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),
33979 y : minY + (this.unitWidth + this.gutter) * 2
33987 * remove a Masonry Brick
33988 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33990 removeBrick : function(brick_id)
33996 for (var i = 0; i<this.bricks.length; i++) {
33997 if (this.bricks[i].id == brick_id) {
33998 this.bricks.splice(i,1);
33999 this.el.dom.removeChild(Roo.get(brick_id).dom);
34006 * adds a Masonry Brick
34007 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34009 addBrick : function(cfg)
34011 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34012 //this.register(cn);
34013 cn.parentId = this.id;
34014 cn.render(this.el);
34019 * register a Masonry Brick
34020 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34023 register : function(brick)
34025 this.bricks.push(brick);
34026 brick.masonryId = this.id;
34030 * clear all the Masonry Brick
34032 clearAll : function()
34035 //this.getChildContainer().dom.innerHTML = "";
34036 this.el.dom.innerHTML = '';
34039 getSelected : function()
34041 if (!this.selectedBrick) {
34045 return this.selectedBrick;
34049 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34053 * register a Masonry Layout
34054 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34057 register : function(layout)
34059 this.groups[layout.id] = layout;
34062 * fetch a Masonry Layout based on the masonry layout ID
34063 * @param {string} the masonry layout to add
34064 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34067 get: function(layout_id) {
34068 if (typeof(this.groups[layout_id]) == 'undefined') {
34071 return this.groups[layout_id] ;
34083 * http://masonry.desandro.com
34085 * The idea is to render all the bricks based on vertical width...
34087 * The original code extends 'outlayer' - we might need to use that....
34093 * @class Roo.bootstrap.LayoutMasonryAuto
34094 * @extends Roo.bootstrap.Component
34095 * Bootstrap Layout Masonry class
34098 * Create a new Element
34099 * @param {Object} config The config object
34102 Roo.bootstrap.LayoutMasonryAuto = function(config){
34103 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34106 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34109 * @cfg {Boolean} isFitWidth - resize the width..
34111 isFitWidth : false, // options..
34113 * @cfg {Boolean} isOriginLeft = left align?
34115 isOriginLeft : true,
34117 * @cfg {Boolean} isOriginTop = top align?
34119 isOriginTop : false,
34121 * @cfg {Boolean} isLayoutInstant = no animation?
34123 isLayoutInstant : false, // needed?
34125 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34127 isResizingContainer : true,
34129 * @cfg {Number} columnWidth width of the columns
34135 * @cfg {Number} maxCols maximum number of columns
34140 * @cfg {Number} padHeight padding below box..
34146 * @cfg {Boolean} isAutoInitial defalut true
34149 isAutoInitial : true,
34155 initialColumnWidth : 0,
34156 currentSize : null,
34158 colYs : null, // array.
34165 bricks: null, //CompositeElement
34166 cols : 0, // array?
34167 // element : null, // wrapped now this.el
34168 _isLayoutInited : null,
34171 getAutoCreate : function(){
34175 cls: 'blog-masonary-wrapper ' + this.cls,
34177 cls : 'mas-boxes masonary'
34184 getChildContainer: function( )
34186 if (this.boxesEl) {
34187 return this.boxesEl;
34190 this.boxesEl = this.el.select('.mas-boxes').first();
34192 return this.boxesEl;
34196 initEvents : function()
34200 if(this.isAutoInitial){
34201 Roo.log('hook children rendered');
34202 this.on('childrenrendered', function() {
34203 Roo.log('children rendered');
34210 initial : function()
34212 this.reloadItems();
34214 this.currentSize = this.el.getBox(true);
34216 /// was window resize... - let's see if this works..
34217 Roo.EventManager.onWindowResize(this.resize, this);
34219 if(!this.isAutoInitial){
34224 this.layout.defer(500,this);
34227 reloadItems: function()
34229 this.bricks = this.el.select('.masonry-brick', true);
34231 this.bricks.each(function(b) {
34232 //Roo.log(b.getSize());
34233 if (!b.attr('originalwidth')) {
34234 b.attr('originalwidth', b.getSize().width);
34239 Roo.log(this.bricks.elements.length);
34242 resize : function()
34245 var cs = this.el.getBox(true);
34247 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34248 Roo.log("no change in with or X");
34251 this.currentSize = cs;
34255 layout : function()
34258 this._resetLayout();
34259 //this._manageStamps();
34261 // don't animate first layout
34262 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34263 this.layoutItems( isInstant );
34265 // flag for initalized
34266 this._isLayoutInited = true;
34269 layoutItems : function( isInstant )
34271 //var items = this._getItemsForLayout( this.items );
34272 // original code supports filtering layout items.. we just ignore it..
34274 this._layoutItems( this.bricks , isInstant );
34276 this._postLayout();
34278 _layoutItems : function ( items , isInstant)
34280 //this.fireEvent( 'layout', this, items );
34283 if ( !items || !items.elements.length ) {
34284 // no items, emit event with empty array
34289 items.each(function(item) {
34290 Roo.log("layout item");
34292 // get x/y object from method
34293 var position = this._getItemLayoutPosition( item );
34295 position.item = item;
34296 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34297 queue.push( position );
34300 this._processLayoutQueue( queue );
34302 /** Sets position of item in DOM
34303 * @param {Element} item
34304 * @param {Number} x - horizontal position
34305 * @param {Number} y - vertical position
34306 * @param {Boolean} isInstant - disables transitions
34308 _processLayoutQueue : function( queue )
34310 for ( var i=0, len = queue.length; i < len; i++ ) {
34311 var obj = queue[i];
34312 obj.item.position('absolute');
34313 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34319 * Any logic you want to do after each layout,
34320 * i.e. size the container
34322 _postLayout : function()
34324 this.resizeContainer();
34327 resizeContainer : function()
34329 if ( !this.isResizingContainer ) {
34332 var size = this._getContainerSize();
34334 this.el.setSize(size.width,size.height);
34335 this.boxesEl.setSize(size.width,size.height);
34341 _resetLayout : function()
34343 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34344 this.colWidth = this.el.getWidth();
34345 //this.gutter = this.el.getWidth();
34347 this.measureColumns();
34353 this.colYs.push( 0 );
34359 measureColumns : function()
34361 this.getContainerWidth();
34362 // if columnWidth is 0, default to outerWidth of first item
34363 if ( !this.columnWidth ) {
34364 var firstItem = this.bricks.first();
34365 Roo.log(firstItem);
34366 this.columnWidth = this.containerWidth;
34367 if (firstItem && firstItem.attr('originalwidth') ) {
34368 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34370 // columnWidth fall back to item of first element
34371 Roo.log("set column width?");
34372 this.initialColumnWidth = this.columnWidth ;
34374 // if first elem has no width, default to size of container
34379 if (this.initialColumnWidth) {
34380 this.columnWidth = this.initialColumnWidth;
34385 // column width is fixed at the top - however if container width get's smaller we should
34388 // this bit calcs how man columns..
34390 var columnWidth = this.columnWidth += this.gutter;
34392 // calculate columns
34393 var containerWidth = this.containerWidth + this.gutter;
34395 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34396 // fix rounding errors, typically with gutters
34397 var excess = columnWidth - containerWidth % columnWidth;
34400 // if overshoot is less than a pixel, round up, otherwise floor it
34401 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34402 cols = Math[ mathMethod ]( cols );
34403 this.cols = Math.max( cols, 1 );
34404 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34406 // padding positioning..
34407 var totalColWidth = this.cols * this.columnWidth;
34408 var padavail = this.containerWidth - totalColWidth;
34409 // so for 2 columns - we need 3 'pads'
34411 var padNeeded = (1+this.cols) * this.padWidth;
34413 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34415 this.columnWidth += padExtra
34416 //this.padWidth = Math.floor(padavail / ( this.cols));
34418 // adjust colum width so that padding is fixed??
34420 // we have 3 columns ... total = width * 3
34421 // we have X left over... that should be used by
34423 //if (this.expandC) {
34431 getContainerWidth : function()
34433 /* // container is parent if fit width
34434 var container = this.isFitWidth ? this.element.parentNode : this.element;
34435 // check that this.size and size are there
34436 // IE8 triggers resize on body size change, so they might not be
34438 var size = getSize( container ); //FIXME
34439 this.containerWidth = size && size.innerWidth; //FIXME
34442 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34446 _getItemLayoutPosition : function( item ) // what is item?
34448 // we resize the item to our columnWidth..
34450 item.setWidth(this.columnWidth);
34451 item.autoBoxAdjust = false;
34453 var sz = item.getSize();
34455 // how many columns does this brick span
34456 var remainder = this.containerWidth % this.columnWidth;
34458 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34459 // round if off by 1 pixel, otherwise use ceil
34460 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34461 colSpan = Math.min( colSpan, this.cols );
34463 // normally this should be '1' as we dont' currently allow multi width columns..
34465 var colGroup = this._getColGroup( colSpan );
34466 // get the minimum Y value from the columns
34467 var minimumY = Math.min.apply( Math, colGroup );
34468 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34470 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34472 // position the brick
34474 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34475 y: this.currentSize.y + minimumY + this.padHeight
34479 // apply setHeight to necessary columns
34480 var setHeight = minimumY + sz.height + this.padHeight;
34481 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34483 var setSpan = this.cols + 1 - colGroup.length;
34484 for ( var i = 0; i < setSpan; i++ ) {
34485 this.colYs[ shortColIndex + i ] = setHeight ;
34492 * @param {Number} colSpan - number of columns the element spans
34493 * @returns {Array} colGroup
34495 _getColGroup : function( colSpan )
34497 if ( colSpan < 2 ) {
34498 // if brick spans only one column, use all the column Ys
34503 // how many different places could this brick fit horizontally
34504 var groupCount = this.cols + 1 - colSpan;
34505 // for each group potential horizontal position
34506 for ( var i = 0; i < groupCount; i++ ) {
34507 // make an array of colY values for that one group
34508 var groupColYs = this.colYs.slice( i, i + colSpan );
34509 // and get the max value of the array
34510 colGroup[i] = Math.max.apply( Math, groupColYs );
34515 _manageStamp : function( stamp )
34517 var stampSize = stamp.getSize();
34518 var offset = stamp.getBox();
34519 // get the columns that this stamp affects
34520 var firstX = this.isOriginLeft ? offset.x : offset.right;
34521 var lastX = firstX + stampSize.width;
34522 var firstCol = Math.floor( firstX / this.columnWidth );
34523 firstCol = Math.max( 0, firstCol );
34525 var lastCol = Math.floor( lastX / this.columnWidth );
34526 // lastCol should not go over if multiple of columnWidth #425
34527 lastCol -= lastX % this.columnWidth ? 0 : 1;
34528 lastCol = Math.min( this.cols - 1, lastCol );
34530 // set colYs to bottom of the stamp
34531 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34534 for ( var i = firstCol; i <= lastCol; i++ ) {
34535 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34540 _getContainerSize : function()
34542 this.maxY = Math.max.apply( Math, this.colYs );
34547 if ( this.isFitWidth ) {
34548 size.width = this._getContainerFitWidth();
34554 _getContainerFitWidth : function()
34556 var unusedCols = 0;
34557 // count unused columns
34560 if ( this.colYs[i] !== 0 ) {
34565 // fit container to columns that have been used
34566 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34569 needsResizeLayout : function()
34571 var previousWidth = this.containerWidth;
34572 this.getContainerWidth();
34573 return previousWidth !== this.containerWidth;
34588 * @class Roo.bootstrap.MasonryBrick
34589 * @extends Roo.bootstrap.Component
34590 * Bootstrap MasonryBrick class
34593 * Create a new MasonryBrick
34594 * @param {Object} config The config object
34597 Roo.bootstrap.MasonryBrick = function(config){
34599 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34601 Roo.bootstrap.MasonryBrick.register(this);
34607 * When a MasonryBrick is clcik
34608 * @param {Roo.bootstrap.MasonryBrick} this
34609 * @param {Roo.EventObject} e
34615 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34618 * @cfg {String} title
34622 * @cfg {String} html
34626 * @cfg {String} bgimage
34630 * @cfg {String} videourl
34634 * @cfg {String} cls
34638 * @cfg {String} href
34642 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34647 * @cfg {String} placetitle (center|bottom)
34652 * @cfg {Boolean} isFitContainer defalut true
34654 isFitContainer : true,
34657 * @cfg {Boolean} preventDefault defalut false
34659 preventDefault : false,
34662 * @cfg {Boolean} inverse defalut false
34664 maskInverse : false,
34666 getAutoCreate : function()
34668 if(!this.isFitContainer){
34669 return this.getSplitAutoCreate();
34672 var cls = 'masonry-brick masonry-brick-full';
34674 if(this.href.length){
34675 cls += ' masonry-brick-link';
34678 if(this.bgimage.length){
34679 cls += ' masonry-brick-image';
34682 if(this.maskInverse){
34683 cls += ' mask-inverse';
34686 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34687 cls += ' enable-mask';
34691 cls += ' masonry-' + this.size + '-brick';
34694 if(this.placetitle.length){
34696 switch (this.placetitle) {
34698 cls += ' masonry-center-title';
34701 cls += ' masonry-bottom-title';
34708 if(!this.html.length && !this.bgimage.length){
34709 cls += ' masonry-center-title';
34712 if(!this.html.length && this.bgimage.length){
34713 cls += ' masonry-bottom-title';
34718 cls += ' ' + this.cls;
34722 tag: (this.href.length) ? 'a' : 'div',
34727 cls: 'masonry-brick-mask'
34731 cls: 'masonry-brick-paragraph',
34737 if(this.href.length){
34738 cfg.href = this.href;
34741 var cn = cfg.cn[1].cn;
34743 if(this.title.length){
34746 cls: 'masonry-brick-title',
34751 if(this.html.length){
34754 cls: 'masonry-brick-text',
34759 if (!this.title.length && !this.html.length) {
34760 cfg.cn[1].cls += ' hide';
34763 if(this.bgimage.length){
34766 cls: 'masonry-brick-image-view',
34771 if(this.videourl.length){
34772 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34773 // youtube support only?
34776 cls: 'masonry-brick-image-view',
34779 allowfullscreen : true
34787 getSplitAutoCreate : function()
34789 var cls = 'masonry-brick masonry-brick-split';
34791 if(this.href.length){
34792 cls += ' masonry-brick-link';
34795 if(this.bgimage.length){
34796 cls += ' masonry-brick-image';
34800 cls += ' masonry-' + this.size + '-brick';
34803 switch (this.placetitle) {
34805 cls += ' masonry-center-title';
34808 cls += ' masonry-bottom-title';
34811 if(!this.bgimage.length){
34812 cls += ' masonry-center-title';
34815 if(this.bgimage.length){
34816 cls += ' masonry-bottom-title';
34822 cls += ' ' + this.cls;
34826 tag: (this.href.length) ? 'a' : 'div',
34831 cls: 'masonry-brick-split-head',
34835 cls: 'masonry-brick-paragraph',
34842 cls: 'masonry-brick-split-body',
34848 if(this.href.length){
34849 cfg.href = this.href;
34852 if(this.title.length){
34853 cfg.cn[0].cn[0].cn.push({
34855 cls: 'masonry-brick-title',
34860 if(this.html.length){
34861 cfg.cn[1].cn.push({
34863 cls: 'masonry-brick-text',
34868 if(this.bgimage.length){
34869 cfg.cn[0].cn.push({
34871 cls: 'masonry-brick-image-view',
34876 if(this.videourl.length){
34877 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34878 // youtube support only?
34879 cfg.cn[0].cn.cn.push({
34881 cls: 'masonry-brick-image-view',
34884 allowfullscreen : true
34891 initEvents: function()
34893 switch (this.size) {
34926 this.el.on('touchstart', this.onTouchStart, this);
34927 this.el.on('touchmove', this.onTouchMove, this);
34928 this.el.on('touchend', this.onTouchEnd, this);
34929 this.el.on('contextmenu', this.onContextMenu, this);
34931 this.el.on('mouseenter' ,this.enter, this);
34932 this.el.on('mouseleave', this.leave, this);
34933 this.el.on('click', this.onClick, this);
34936 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34937 this.parent().bricks.push(this);
34942 onClick: function(e, el)
34944 var time = this.endTimer - this.startTimer;
34945 // Roo.log(e.preventDefault());
34948 e.preventDefault();
34953 if(!this.preventDefault){
34957 e.preventDefault();
34959 if (this.activeClass != '') {
34960 this.selectBrick();
34963 this.fireEvent('click', this, e);
34966 enter: function(e, el)
34968 e.preventDefault();
34970 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34974 if(this.bgimage.length && this.html.length){
34975 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34979 leave: function(e, el)
34981 e.preventDefault();
34983 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34987 if(this.bgimage.length && this.html.length){
34988 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34992 onTouchStart: function(e, el)
34994 // e.preventDefault();
34996 this.touchmoved = false;
34998 if(!this.isFitContainer){
35002 if(!this.bgimage.length || !this.html.length){
35006 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35008 this.timer = new Date().getTime();
35012 onTouchMove: function(e, el)
35014 this.touchmoved = true;
35017 onContextMenu : function(e,el)
35019 e.preventDefault();
35020 e.stopPropagation();
35024 onTouchEnd: function(e, el)
35026 // e.preventDefault();
35028 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35035 if(!this.bgimage.length || !this.html.length){
35037 if(this.href.length){
35038 window.location.href = this.href;
35044 if(!this.isFitContainer){
35048 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35050 window.location.href = this.href;
35053 //selection on single brick only
35054 selectBrick : function() {
35056 if (!this.parentId) {
35060 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35061 var index = m.selectedBrick.indexOf(this.id);
35064 m.selectedBrick.splice(index,1);
35065 this.el.removeClass(this.activeClass);
35069 for(var i = 0; i < m.selectedBrick.length; i++) {
35070 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35071 b.el.removeClass(b.activeClass);
35074 m.selectedBrick = [];
35076 m.selectedBrick.push(this.id);
35077 this.el.addClass(this.activeClass);
35081 isSelected : function(){
35082 return this.el.hasClass(this.activeClass);
35087 Roo.apply(Roo.bootstrap.MasonryBrick, {
35090 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35092 * register a Masonry Brick
35093 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35096 register : function(brick)
35098 //this.groups[brick.id] = brick;
35099 this.groups.add(brick.id, brick);
35102 * fetch a masonry brick based on the masonry brick ID
35103 * @param {string} the masonry brick to add
35104 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35107 get: function(brick_id)
35109 // if (typeof(this.groups[brick_id]) == 'undefined') {
35112 // return this.groups[brick_id] ;
35114 if(this.groups.key(brick_id)) {
35115 return this.groups.key(brick_id);
35133 * @class Roo.bootstrap.Brick
35134 * @extends Roo.bootstrap.Component
35135 * Bootstrap Brick class
35138 * Create a new Brick
35139 * @param {Object} config The config object
35142 Roo.bootstrap.Brick = function(config){
35143 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35149 * When a Brick is click
35150 * @param {Roo.bootstrap.Brick} this
35151 * @param {Roo.EventObject} e
35157 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35160 * @cfg {String} title
35164 * @cfg {String} html
35168 * @cfg {String} bgimage
35172 * @cfg {String} cls
35176 * @cfg {String} href
35180 * @cfg {String} video
35184 * @cfg {Boolean} square
35188 getAutoCreate : function()
35190 var cls = 'roo-brick';
35192 if(this.href.length){
35193 cls += ' roo-brick-link';
35196 if(this.bgimage.length){
35197 cls += ' roo-brick-image';
35200 if(!this.html.length && !this.bgimage.length){
35201 cls += ' roo-brick-center-title';
35204 if(!this.html.length && this.bgimage.length){
35205 cls += ' roo-brick-bottom-title';
35209 cls += ' ' + this.cls;
35213 tag: (this.href.length) ? 'a' : 'div',
35218 cls: 'roo-brick-paragraph',
35224 if(this.href.length){
35225 cfg.href = this.href;
35228 var cn = cfg.cn[0].cn;
35230 if(this.title.length){
35233 cls: 'roo-brick-title',
35238 if(this.html.length){
35241 cls: 'roo-brick-text',
35248 if(this.bgimage.length){
35251 cls: 'roo-brick-image-view',
35259 initEvents: function()
35261 if(this.title.length || this.html.length){
35262 this.el.on('mouseenter' ,this.enter, this);
35263 this.el.on('mouseleave', this.leave, this);
35266 Roo.EventManager.onWindowResize(this.resize, this);
35268 if(this.bgimage.length){
35269 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35270 this.imageEl.on('load', this.onImageLoad, this);
35277 onImageLoad : function()
35282 resize : function()
35284 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35286 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35288 if(this.bgimage.length){
35289 var image = this.el.select('.roo-brick-image-view', true).first();
35291 image.setWidth(paragraph.getWidth());
35294 image.setHeight(paragraph.getWidth());
35297 this.el.setHeight(image.getHeight());
35298 paragraph.setHeight(image.getHeight());
35304 enter: function(e, el)
35306 e.preventDefault();
35308 if(this.bgimage.length){
35309 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35310 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35314 leave: function(e, el)
35316 e.preventDefault();
35318 if(this.bgimage.length){
35319 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35320 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35335 * @class Roo.bootstrap.NumberField
35336 * @extends Roo.bootstrap.Input
35337 * Bootstrap NumberField class
35343 * Create a new NumberField
35344 * @param {Object} config The config object
35347 Roo.bootstrap.NumberField = function(config){
35348 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35351 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35354 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35356 allowDecimals : true,
35358 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35360 decimalSeparator : ".",
35362 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35364 decimalPrecision : 2,
35366 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35368 allowNegative : true,
35371 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35375 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35377 minValue : Number.NEGATIVE_INFINITY,
35379 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35381 maxValue : Number.MAX_VALUE,
35383 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35385 minText : "The minimum value for this field is {0}",
35387 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35389 maxText : "The maximum value for this field is {0}",
35391 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35392 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35394 nanText : "{0} is not a valid number",
35396 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35398 thousandsDelimiter : false,
35400 * @cfg {String} valueAlign alignment of value
35402 valueAlign : "left",
35404 getAutoCreate : function()
35406 var hiddenInput = {
35410 cls: 'hidden-number-input'
35414 hiddenInput.name = this.name;
35419 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35421 this.name = hiddenInput.name;
35423 if(cfg.cn.length > 0) {
35424 cfg.cn.push(hiddenInput);
35431 initEvents : function()
35433 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35435 var allowed = "0123456789";
35437 if(this.allowDecimals){
35438 allowed += this.decimalSeparator;
35441 if(this.allowNegative){
35445 if(this.thousandsDelimiter) {
35449 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35451 var keyPress = function(e){
35453 var k = e.getKey();
35455 var c = e.getCharCode();
35458 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35459 allowed.indexOf(String.fromCharCode(c)) === -1
35465 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35469 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35474 this.el.on("keypress", keyPress, this);
35477 validateValue : function(value)
35480 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35484 var num = this.parseValue(value);
35487 this.markInvalid(String.format(this.nanText, value));
35491 if(num < this.minValue){
35492 this.markInvalid(String.format(this.minText, this.minValue));
35496 if(num > this.maxValue){
35497 this.markInvalid(String.format(this.maxText, this.maxValue));
35504 getValue : function()
35506 var v = this.hiddenEl().getValue();
35508 return this.fixPrecision(this.parseValue(v));
35511 parseValue : function(value)
35513 if(this.thousandsDelimiter) {
35515 r = new RegExp(",", "g");
35516 value = value.replace(r, "");
35519 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35520 return isNaN(value) ? '' : value;
35523 fixPrecision : function(value)
35525 if(this.thousandsDelimiter) {
35527 r = new RegExp(",", "g");
35528 value = value.replace(r, "");
35531 var nan = isNaN(value);
35533 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35534 return nan ? '' : value;
35536 return parseFloat(value).toFixed(this.decimalPrecision);
35539 setValue : function(v)
35541 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35547 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35549 this.inputEl().dom.value = (v == '') ? '' :
35550 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35552 if(!this.allowZero && v === '0') {
35553 this.hiddenEl().dom.value = '';
35554 this.inputEl().dom.value = '';
35561 decimalPrecisionFcn : function(v)
35563 return Math.floor(v);
35566 beforeBlur : function()
35568 var v = this.parseValue(this.getRawValue());
35570 if(v || v === 0 || v === ''){
35575 hiddenEl : function()
35577 return this.el.select('input.hidden-number-input',true).first();
35589 * @class Roo.bootstrap.DocumentSlider
35590 * @extends Roo.bootstrap.Component
35591 * Bootstrap DocumentSlider class
35594 * Create a new DocumentViewer
35595 * @param {Object} config The config object
35598 Roo.bootstrap.DocumentSlider = function(config){
35599 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35606 * Fire after initEvent
35607 * @param {Roo.bootstrap.DocumentSlider} this
35612 * Fire after update
35613 * @param {Roo.bootstrap.DocumentSlider} this
35619 * @param {Roo.bootstrap.DocumentSlider} this
35625 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35631 getAutoCreate : function()
35635 cls : 'roo-document-slider',
35639 cls : 'roo-document-slider-header',
35643 cls : 'roo-document-slider-header-title'
35649 cls : 'roo-document-slider-body',
35653 cls : 'roo-document-slider-prev',
35657 cls : 'fa fa-chevron-left'
35663 cls : 'roo-document-slider-thumb',
35667 cls : 'roo-document-slider-image'
35673 cls : 'roo-document-slider-next',
35677 cls : 'fa fa-chevron-right'
35689 initEvents : function()
35691 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35692 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35694 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35695 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35697 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35698 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35700 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35701 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35703 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35704 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35706 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35707 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35709 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35710 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35712 this.thumbEl.on('click', this.onClick, this);
35714 this.prevIndicator.on('click', this.prev, this);
35716 this.nextIndicator.on('click', this.next, this);
35720 initial : function()
35722 if(this.files.length){
35723 this.indicator = 1;
35727 this.fireEvent('initial', this);
35730 update : function()
35732 this.imageEl.attr('src', this.files[this.indicator - 1]);
35734 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35736 this.prevIndicator.show();
35738 if(this.indicator == 1){
35739 this.prevIndicator.hide();
35742 this.nextIndicator.show();
35744 if(this.indicator == this.files.length){
35745 this.nextIndicator.hide();
35748 this.thumbEl.scrollTo('top');
35750 this.fireEvent('update', this);
35753 onClick : function(e)
35755 e.preventDefault();
35757 this.fireEvent('click', this);
35762 e.preventDefault();
35764 this.indicator = Math.max(1, this.indicator - 1);
35771 e.preventDefault();
35773 this.indicator = Math.min(this.files.length, this.indicator + 1);
35787 * @class Roo.bootstrap.RadioSet
35788 * @extends Roo.bootstrap.Input
35789 * Bootstrap RadioSet class
35790 * @cfg {String} indicatorpos (left|right) default left
35791 * @cfg {Boolean} inline (true|false) inline the element (default true)
35792 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35794 * Create a new RadioSet
35795 * @param {Object} config The config object
35798 Roo.bootstrap.RadioSet = function(config){
35800 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35804 Roo.bootstrap.RadioSet.register(this);
35809 * Fires when the element is checked or unchecked.
35810 * @param {Roo.bootstrap.RadioSet} this This radio
35811 * @param {Roo.bootstrap.Radio} item The checked item
35816 * Fires when the element is click.
35817 * @param {Roo.bootstrap.RadioSet} this This radio set
35818 * @param {Roo.bootstrap.Radio} item The checked item
35819 * @param {Roo.EventObject} e The event object
35826 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35834 indicatorpos : 'left',
35836 getAutoCreate : function()
35840 cls : 'roo-radio-set-label',
35844 html : this.fieldLabel
35848 if (Roo.bootstrap.version == 3) {
35851 if(this.indicatorpos == 'left'){
35854 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35855 tooltip : 'This field is required'
35860 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35861 tooltip : 'This field is required'
35867 cls : 'roo-radio-set-items'
35870 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35872 if (align === 'left' && this.fieldLabel.length) {
35875 cls : "roo-radio-set-right",
35881 if(this.labelWidth > 12){
35882 label.style = "width: " + this.labelWidth + 'px';
35885 if(this.labelWidth < 13 && this.labelmd == 0){
35886 this.labelmd = this.labelWidth;
35889 if(this.labellg > 0){
35890 label.cls += ' col-lg-' + this.labellg;
35891 items.cls += ' col-lg-' + (12 - this.labellg);
35894 if(this.labelmd > 0){
35895 label.cls += ' col-md-' + this.labelmd;
35896 items.cls += ' col-md-' + (12 - this.labelmd);
35899 if(this.labelsm > 0){
35900 label.cls += ' col-sm-' + this.labelsm;
35901 items.cls += ' col-sm-' + (12 - this.labelsm);
35904 if(this.labelxs > 0){
35905 label.cls += ' col-xs-' + this.labelxs;
35906 items.cls += ' col-xs-' + (12 - this.labelxs);
35912 cls : 'roo-radio-set',
35916 cls : 'roo-radio-set-input',
35919 value : this.value ? this.value : ''
35926 if(this.weight.length){
35927 cfg.cls += ' roo-radio-' + this.weight;
35931 cfg.cls += ' roo-radio-set-inline';
35935 ['xs','sm','md','lg'].map(function(size){
35936 if (settings[size]) {
35937 cfg.cls += ' col-' + size + '-' + settings[size];
35945 initEvents : function()
35947 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35948 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35950 if(!this.fieldLabel.length){
35951 this.labelEl.hide();
35954 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35955 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35957 this.indicator = this.indicatorEl();
35959 if(this.indicator){
35960 this.indicator.addClass('invisible');
35963 this.originalValue = this.getValue();
35967 inputEl: function ()
35969 return this.el.select('.roo-radio-set-input', true).first();
35972 getChildContainer : function()
35974 return this.itemsEl;
35977 register : function(item)
35979 this.radioes.push(item);
35983 validate : function()
35985 if(this.getVisibilityEl().hasClass('hidden')){
35991 Roo.each(this.radioes, function(i){
36000 if(this.allowBlank) {
36004 if(this.disabled || valid){
36009 this.markInvalid();
36014 markValid : function()
36016 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36017 this.indicatorEl().removeClass('visible');
36018 this.indicatorEl().addClass('invisible');
36022 if (Roo.bootstrap.version == 3) {
36023 this.el.removeClass([this.invalidClass, this.validClass]);
36024 this.el.addClass(this.validClass);
36026 this.el.removeClass(['is-invalid','is-valid']);
36027 this.el.addClass(['is-valid']);
36029 this.fireEvent('valid', this);
36032 markInvalid : function(msg)
36034 if(this.allowBlank || this.disabled){
36038 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36039 this.indicatorEl().removeClass('invisible');
36040 this.indicatorEl().addClass('visible');
36042 if (Roo.bootstrap.version == 3) {
36043 this.el.removeClass([this.invalidClass, this.validClass]);
36044 this.el.addClass(this.invalidClass);
36046 this.el.removeClass(['is-invalid','is-valid']);
36047 this.el.addClass(['is-invalid']);
36050 this.fireEvent('invalid', this, msg);
36054 setValue : function(v, suppressEvent)
36056 if(this.value === v){
36063 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36066 Roo.each(this.radioes, function(i){
36068 i.el.removeClass('checked');
36071 Roo.each(this.radioes, function(i){
36073 if(i.value === v || i.value.toString() === v.toString()){
36075 i.el.addClass('checked');
36077 if(suppressEvent !== true){
36078 this.fireEvent('check', this, i);
36089 clearInvalid : function(){
36091 if(!this.el || this.preventMark){
36095 this.el.removeClass([this.invalidClass]);
36097 this.fireEvent('valid', this);
36102 Roo.apply(Roo.bootstrap.RadioSet, {
36106 register : function(set)
36108 this.groups[set.name] = set;
36111 get: function(name)
36113 if (typeof(this.groups[name]) == 'undefined') {
36117 return this.groups[name] ;
36123 * Ext JS Library 1.1.1
36124 * Copyright(c) 2006-2007, Ext JS, LLC.
36126 * Originally Released Under LGPL - original licence link has changed is not relivant.
36129 * <script type="text/javascript">
36134 * @class Roo.bootstrap.SplitBar
36135 * @extends Roo.util.Observable
36136 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36140 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36141 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36142 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36143 split.minSize = 100;
36144 split.maxSize = 600;
36145 split.animate = true;
36146 split.on('moved', splitterMoved);
36149 * Create a new SplitBar
36150 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36151 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36152 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36153 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36154 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36155 position of the SplitBar).
36157 Roo.bootstrap.SplitBar = function(cfg){
36162 // dragElement : elm
36163 // resizingElement: el,
36165 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36166 // placement : Roo.bootstrap.SplitBar.LEFT ,
36167 // existingProxy ???
36170 this.el = Roo.get(cfg.dragElement, true);
36171 this.el.dom.unselectable = "on";
36173 this.resizingEl = Roo.get(cfg.resizingElement, true);
36177 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36178 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36181 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36184 * The minimum size of the resizing element. (Defaults to 0)
36190 * The maximum size of the resizing element. (Defaults to 2000)
36193 this.maxSize = 2000;
36196 * Whether to animate the transition to the new size
36199 this.animate = false;
36202 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36205 this.useShim = false;
36210 if(!cfg.existingProxy){
36212 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36214 this.proxy = Roo.get(cfg.existingProxy).dom;
36217 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36220 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36223 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36226 this.dragSpecs = {};
36229 * @private The adapter to use to positon and resize elements
36231 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36232 this.adapter.init(this);
36234 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36236 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36237 this.el.addClass("roo-splitbar-h");
36240 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36241 this.el.addClass("roo-splitbar-v");
36247 * Fires when the splitter is moved (alias for {@link #event-moved})
36248 * @param {Roo.bootstrap.SplitBar} this
36249 * @param {Number} newSize the new width or height
36254 * Fires when the splitter is moved
36255 * @param {Roo.bootstrap.SplitBar} this
36256 * @param {Number} newSize the new width or height
36260 * @event beforeresize
36261 * Fires before the splitter is dragged
36262 * @param {Roo.bootstrap.SplitBar} this
36264 "beforeresize" : true,
36266 "beforeapply" : true
36269 Roo.util.Observable.call(this);
36272 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36273 onStartProxyDrag : function(x, y){
36274 this.fireEvent("beforeresize", this);
36276 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36278 o.enableDisplayMode("block");
36279 // all splitbars share the same overlay
36280 Roo.bootstrap.SplitBar.prototype.overlay = o;
36282 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36283 this.overlay.show();
36284 Roo.get(this.proxy).setDisplayed("block");
36285 var size = this.adapter.getElementSize(this);
36286 this.activeMinSize = this.getMinimumSize();;
36287 this.activeMaxSize = this.getMaximumSize();;
36288 var c1 = size - this.activeMinSize;
36289 var c2 = Math.max(this.activeMaxSize - size, 0);
36290 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36291 this.dd.resetConstraints();
36292 this.dd.setXConstraint(
36293 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36294 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36296 this.dd.setYConstraint(0, 0);
36298 this.dd.resetConstraints();
36299 this.dd.setXConstraint(0, 0);
36300 this.dd.setYConstraint(
36301 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36302 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36305 this.dragSpecs.startSize = size;
36306 this.dragSpecs.startPoint = [x, y];
36307 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36311 * @private Called after the drag operation by the DDProxy
36313 onEndProxyDrag : function(e){
36314 Roo.get(this.proxy).setDisplayed(false);
36315 var endPoint = Roo.lib.Event.getXY(e);
36317 this.overlay.hide();
36320 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36321 newSize = this.dragSpecs.startSize +
36322 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36323 endPoint[0] - this.dragSpecs.startPoint[0] :
36324 this.dragSpecs.startPoint[0] - endPoint[0]
36327 newSize = this.dragSpecs.startSize +
36328 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36329 endPoint[1] - this.dragSpecs.startPoint[1] :
36330 this.dragSpecs.startPoint[1] - endPoint[1]
36333 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36334 if(newSize != this.dragSpecs.startSize){
36335 if(this.fireEvent('beforeapply', this, newSize) !== false){
36336 this.adapter.setElementSize(this, newSize);
36337 this.fireEvent("moved", this, newSize);
36338 this.fireEvent("resize", this, newSize);
36344 * Get the adapter this SplitBar uses
36345 * @return The adapter object
36347 getAdapter : function(){
36348 return this.adapter;
36352 * Set the adapter this SplitBar uses
36353 * @param {Object} adapter A SplitBar adapter object
36355 setAdapter : function(adapter){
36356 this.adapter = adapter;
36357 this.adapter.init(this);
36361 * Gets the minimum size for the resizing element
36362 * @return {Number} The minimum size
36364 getMinimumSize : function(){
36365 return this.minSize;
36369 * Sets the minimum size for the resizing element
36370 * @param {Number} minSize The minimum size
36372 setMinimumSize : function(minSize){
36373 this.minSize = minSize;
36377 * Gets the maximum size for the resizing element
36378 * @return {Number} The maximum size
36380 getMaximumSize : function(){
36381 return this.maxSize;
36385 * Sets the maximum size for the resizing element
36386 * @param {Number} maxSize The maximum size
36388 setMaximumSize : function(maxSize){
36389 this.maxSize = maxSize;
36393 * Sets the initialize size for the resizing element
36394 * @param {Number} size The initial size
36396 setCurrentSize : function(size){
36397 var oldAnimate = this.animate;
36398 this.animate = false;
36399 this.adapter.setElementSize(this, size);
36400 this.animate = oldAnimate;
36404 * Destroy this splitbar.
36405 * @param {Boolean} removeEl True to remove the element
36407 destroy : function(removeEl){
36409 this.shim.remove();
36412 this.proxy.parentNode.removeChild(this.proxy);
36420 * @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.
36422 Roo.bootstrap.SplitBar.createProxy = function(dir){
36423 var proxy = new Roo.Element(document.createElement("div"));
36424 proxy.unselectable();
36425 var cls = 'roo-splitbar-proxy';
36426 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36427 document.body.appendChild(proxy.dom);
36432 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36433 * Default Adapter. It assumes the splitter and resizing element are not positioned
36434 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36436 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36439 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36440 // do nothing for now
36441 init : function(s){
36445 * Called before drag operations to get the current size of the resizing element.
36446 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36448 getElementSize : function(s){
36449 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36450 return s.resizingEl.getWidth();
36452 return s.resizingEl.getHeight();
36457 * Called after drag operations to set the size of the resizing element.
36458 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36459 * @param {Number} newSize The new size to set
36460 * @param {Function} onComplete A function to be invoked when resizing is complete
36462 setElementSize : function(s, newSize, onComplete){
36463 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36465 s.resizingEl.setWidth(newSize);
36467 onComplete(s, newSize);
36470 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36475 s.resizingEl.setHeight(newSize);
36477 onComplete(s, newSize);
36480 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36487 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36488 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36489 * Adapter that moves the splitter element to align with the resized sizing element.
36490 * Used with an absolute positioned SplitBar.
36491 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36492 * document.body, make sure you assign an id to the body element.
36494 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36495 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36496 this.container = Roo.get(container);
36499 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36500 init : function(s){
36501 this.basic.init(s);
36504 getElementSize : function(s){
36505 return this.basic.getElementSize(s);
36508 setElementSize : function(s, newSize, onComplete){
36509 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36512 moveSplitter : function(s){
36513 var yes = Roo.bootstrap.SplitBar;
36514 switch(s.placement){
36516 s.el.setX(s.resizingEl.getRight());
36519 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36522 s.el.setY(s.resizingEl.getBottom());
36525 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36532 * Orientation constant - Create a vertical SplitBar
36536 Roo.bootstrap.SplitBar.VERTICAL = 1;
36539 * Orientation constant - Create a horizontal SplitBar
36543 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36546 * Placement constant - The resizing element is to the left of the splitter element
36550 Roo.bootstrap.SplitBar.LEFT = 1;
36553 * Placement constant - The resizing element is to the right of the splitter element
36557 Roo.bootstrap.SplitBar.RIGHT = 2;
36560 * Placement constant - The resizing element is positioned above the splitter element
36564 Roo.bootstrap.SplitBar.TOP = 3;
36567 * Placement constant - The resizing element is positioned under splitter element
36571 Roo.bootstrap.SplitBar.BOTTOM = 4;
36572 Roo.namespace("Roo.bootstrap.layout");/*
36574 * Ext JS Library 1.1.1
36575 * Copyright(c) 2006-2007, Ext JS, LLC.
36577 * Originally Released Under LGPL - original licence link has changed is not relivant.
36580 * <script type="text/javascript">
36584 * @class Roo.bootstrap.layout.Manager
36585 * @extends Roo.bootstrap.Component
36586 * Base class for layout managers.
36588 Roo.bootstrap.layout.Manager = function(config)
36590 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36596 /** false to disable window resize monitoring @type Boolean */
36597 this.monitorWindowResize = true;
36602 * Fires when a layout is performed.
36603 * @param {Roo.LayoutManager} this
36607 * @event regionresized
36608 * Fires when the user resizes a region.
36609 * @param {Roo.LayoutRegion} region The resized region
36610 * @param {Number} newSize The new size (width for east/west, height for north/south)
36612 "regionresized" : true,
36614 * @event regioncollapsed
36615 * Fires when a region is collapsed.
36616 * @param {Roo.LayoutRegion} region The collapsed region
36618 "regioncollapsed" : true,
36620 * @event regionexpanded
36621 * Fires when a region is expanded.
36622 * @param {Roo.LayoutRegion} region The expanded region
36624 "regionexpanded" : true
36626 this.updating = false;
36629 this.el = Roo.get(config.el);
36635 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36640 monitorWindowResize : true,
36646 onRender : function(ct, position)
36649 this.el = Roo.get(ct);
36652 //this.fireEvent('render',this);
36656 initEvents: function()
36660 // ie scrollbar fix
36661 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36662 document.body.scroll = "no";
36663 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36664 this.el.position('relative');
36666 this.id = this.el.id;
36667 this.el.addClass("roo-layout-container");
36668 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36669 if(this.el.dom != document.body ) {
36670 this.el.on('resize', this.layout,this);
36671 this.el.on('show', this.layout,this);
36677 * Returns true if this layout is currently being updated
36678 * @return {Boolean}
36680 isUpdating : function(){
36681 return this.updating;
36685 * Suspend the LayoutManager from doing auto-layouts while
36686 * making multiple add or remove calls
36688 beginUpdate : function(){
36689 this.updating = true;
36693 * Restore auto-layouts and optionally disable the manager from performing a layout
36694 * @param {Boolean} noLayout true to disable a layout update
36696 endUpdate : function(noLayout){
36697 this.updating = false;
36703 layout: function(){
36707 onRegionResized : function(region, newSize){
36708 this.fireEvent("regionresized", region, newSize);
36712 onRegionCollapsed : function(region){
36713 this.fireEvent("regioncollapsed", region);
36716 onRegionExpanded : function(region){
36717 this.fireEvent("regionexpanded", region);
36721 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36722 * performs box-model adjustments.
36723 * @return {Object} The size as an object {width: (the width), height: (the height)}
36725 getViewSize : function()
36728 if(this.el.dom != document.body){
36729 size = this.el.getSize();
36731 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36733 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36734 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36739 * Returns the Element this layout is bound to.
36740 * @return {Roo.Element}
36742 getEl : function(){
36747 * Returns the specified region.
36748 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36749 * @return {Roo.LayoutRegion}
36751 getRegion : function(target){
36752 return this.regions[target.toLowerCase()];
36755 onWindowResize : function(){
36756 if(this.monitorWindowResize){
36763 * Ext JS Library 1.1.1
36764 * Copyright(c) 2006-2007, Ext JS, LLC.
36766 * Originally Released Under LGPL - original licence link has changed is not relivant.
36769 * <script type="text/javascript">
36772 * @class Roo.bootstrap.layout.Border
36773 * @extends Roo.bootstrap.layout.Manager
36774 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36775 * please see: examples/bootstrap/nested.html<br><br>
36777 <b>The container the layout is rendered into can be either the body element or any other element.
36778 If it is not the body element, the container needs to either be an absolute positioned element,
36779 or you will need to add "position:relative" to the css of the container. You will also need to specify
36780 the container size if it is not the body element.</b>
36783 * Create a new Border
36784 * @param {Object} config Configuration options
36786 Roo.bootstrap.layout.Border = function(config){
36787 config = config || {};
36788 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36792 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36793 if(config[region]){
36794 config[region].region = region;
36795 this.addRegion(config[region]);
36801 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36803 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36805 parent : false, // this might point to a 'nest' or a ???
36808 * Creates and adds a new region if it doesn't already exist.
36809 * @param {String} target The target region key (north, south, east, west or center).
36810 * @param {Object} config The regions config object
36811 * @return {BorderLayoutRegion} The new region
36813 addRegion : function(config)
36815 if(!this.regions[config.region]){
36816 var r = this.factory(config);
36817 this.bindRegion(r);
36819 return this.regions[config.region];
36823 bindRegion : function(r){
36824 this.regions[r.config.region] = r;
36826 r.on("visibilitychange", this.layout, this);
36827 r.on("paneladded", this.layout, this);
36828 r.on("panelremoved", this.layout, this);
36829 r.on("invalidated", this.layout, this);
36830 r.on("resized", this.onRegionResized, this);
36831 r.on("collapsed", this.onRegionCollapsed, this);
36832 r.on("expanded", this.onRegionExpanded, this);
36836 * Performs a layout update.
36838 layout : function()
36840 if(this.updating) {
36844 // render all the rebions if they have not been done alreayd?
36845 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36846 if(this.regions[region] && !this.regions[region].bodyEl){
36847 this.regions[region].onRender(this.el)
36851 var size = this.getViewSize();
36852 var w = size.width;
36853 var h = size.height;
36858 //var x = 0, y = 0;
36860 var rs = this.regions;
36861 var north = rs["north"];
36862 var south = rs["south"];
36863 var west = rs["west"];
36864 var east = rs["east"];
36865 var center = rs["center"];
36866 //if(this.hideOnLayout){ // not supported anymore
36867 //c.el.setStyle("display", "none");
36869 if(north && north.isVisible()){
36870 var b = north.getBox();
36871 var m = north.getMargins();
36872 b.width = w - (m.left+m.right);
36875 centerY = b.height + b.y + m.bottom;
36876 centerH -= centerY;
36877 north.updateBox(this.safeBox(b));
36879 if(south && south.isVisible()){
36880 var b = south.getBox();
36881 var m = south.getMargins();
36882 b.width = w - (m.left+m.right);
36884 var totalHeight = (b.height + m.top + m.bottom);
36885 b.y = h - totalHeight + m.top;
36886 centerH -= totalHeight;
36887 south.updateBox(this.safeBox(b));
36889 if(west && west.isVisible()){
36890 var b = west.getBox();
36891 var m = west.getMargins();
36892 b.height = centerH - (m.top+m.bottom);
36894 b.y = centerY + m.top;
36895 var totalWidth = (b.width + m.left + m.right);
36896 centerX += totalWidth;
36897 centerW -= totalWidth;
36898 west.updateBox(this.safeBox(b));
36900 if(east && east.isVisible()){
36901 var b = east.getBox();
36902 var m = east.getMargins();
36903 b.height = centerH - (m.top+m.bottom);
36904 var totalWidth = (b.width + m.left + m.right);
36905 b.x = w - totalWidth + m.left;
36906 b.y = centerY + m.top;
36907 centerW -= totalWidth;
36908 east.updateBox(this.safeBox(b));
36911 var m = center.getMargins();
36913 x: centerX + m.left,
36914 y: centerY + m.top,
36915 width: centerW - (m.left+m.right),
36916 height: centerH - (m.top+m.bottom)
36918 //if(this.hideOnLayout){
36919 //center.el.setStyle("display", "block");
36921 center.updateBox(this.safeBox(centerBox));
36924 this.fireEvent("layout", this);
36928 safeBox : function(box){
36929 box.width = Math.max(0, box.width);
36930 box.height = Math.max(0, box.height);
36935 * Adds a ContentPanel (or subclass) to this layout.
36936 * @param {String} target The target region key (north, south, east, west or center).
36937 * @param {Roo.ContentPanel} panel The panel to add
36938 * @return {Roo.ContentPanel} The added panel
36940 add : function(target, panel){
36942 target = target.toLowerCase();
36943 return this.regions[target].add(panel);
36947 * Remove a ContentPanel (or subclass) to this layout.
36948 * @param {String} target The target region key (north, south, east, west or center).
36949 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36950 * @return {Roo.ContentPanel} The removed panel
36952 remove : function(target, panel){
36953 target = target.toLowerCase();
36954 return this.regions[target].remove(panel);
36958 * Searches all regions for a panel with the specified id
36959 * @param {String} panelId
36960 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36962 findPanel : function(panelId){
36963 var rs = this.regions;
36964 for(var target in rs){
36965 if(typeof rs[target] != "function"){
36966 var p = rs[target].getPanel(panelId);
36976 * Searches all regions for a panel with the specified id and activates (shows) it.
36977 * @param {String/ContentPanel} panelId The panels id or the panel itself
36978 * @return {Roo.ContentPanel} The shown panel or null
36980 showPanel : function(panelId) {
36981 var rs = this.regions;
36982 for(var target in rs){
36983 var r = rs[target];
36984 if(typeof r != "function"){
36985 if(r.hasPanel(panelId)){
36986 return r.showPanel(panelId);
36994 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36995 * @param {Roo.state.Provider} provider (optional) An alternate state provider
36998 restoreState : function(provider){
37000 provider = Roo.state.Manager;
37002 var sm = new Roo.LayoutStateManager();
37003 sm.init(this, provider);
37009 * Adds a xtype elements to the layout.
37013 xtype : 'ContentPanel',
37020 xtype : 'NestedLayoutPanel',
37026 items : [ ... list of content panels or nested layout panels.. ]
37030 * @param {Object} cfg Xtype definition of item to add.
37032 addxtype : function(cfg)
37034 // basically accepts a pannel...
37035 // can accept a layout region..!?!?
37036 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37039 // theory? children can only be panels??
37041 //if (!cfg.xtype.match(/Panel$/)) {
37046 if (typeof(cfg.region) == 'undefined') {
37047 Roo.log("Failed to add Panel, region was not set");
37051 var region = cfg.region;
37057 xitems = cfg.items;
37062 if ( region == 'center') {
37063 Roo.log("Center: " + cfg.title);
37069 case 'Content': // ContentPanel (el, cfg)
37070 case 'Scroll': // ContentPanel (el, cfg)
37072 cfg.autoCreate = cfg.autoCreate || true;
37073 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37075 // var el = this.el.createChild();
37076 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37079 this.add(region, ret);
37083 case 'TreePanel': // our new panel!
37084 cfg.el = this.el.createChild();
37085 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37086 this.add(region, ret);
37091 // create a new Layout (which is a Border Layout...
37093 var clayout = cfg.layout;
37094 clayout.el = this.el.createChild();
37095 clayout.items = clayout.items || [];
37099 // replace this exitems with the clayout ones..
37100 xitems = clayout.items;
37102 // force background off if it's in center...
37103 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37104 cfg.background = false;
37106 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37109 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37110 //console.log('adding nested layout panel ' + cfg.toSource());
37111 this.add(region, ret);
37112 nb = {}; /// find first...
37117 // needs grid and region
37119 //var el = this.getRegion(region).el.createChild();
37121 *var el = this.el.createChild();
37122 // create the grid first...
37123 cfg.grid.container = el;
37124 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37127 if (region == 'center' && this.active ) {
37128 cfg.background = false;
37131 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37133 this.add(region, ret);
37135 if (cfg.background) {
37136 // render grid on panel activation (if panel background)
37137 ret.on('activate', function(gp) {
37138 if (!gp.grid.rendered) {
37139 // gp.grid.render(el);
37143 // cfg.grid.render(el);
37149 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37150 // it was the old xcomponent building that caused this before.
37151 // espeically if border is the top element in the tree.
37161 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37163 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37164 this.add(region, ret);
37168 throw "Can not add '" + cfg.xtype + "' to Border";
37174 this.beginUpdate();
37178 Roo.each(xitems, function(i) {
37179 region = nb && i.region ? i.region : false;
37181 var add = ret.addxtype(i);
37184 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37185 if (!i.background) {
37186 abn[region] = nb[region] ;
37193 // make the last non-background panel active..
37194 //if (nb) { Roo.log(abn); }
37197 for(var r in abn) {
37198 region = this.getRegion(r);
37200 // tried using nb[r], but it does not work..
37202 region.showPanel(abn[r]);
37213 factory : function(cfg)
37216 var validRegions = Roo.bootstrap.layout.Border.regions;
37218 var target = cfg.region;
37221 var r = Roo.bootstrap.layout;
37225 return new r.North(cfg);
37227 return new r.South(cfg);
37229 return new r.East(cfg);
37231 return new r.West(cfg);
37233 return new r.Center(cfg);
37235 throw 'Layout region "'+target+'" not supported.';
37242 * Ext JS Library 1.1.1
37243 * Copyright(c) 2006-2007, Ext JS, LLC.
37245 * Originally Released Under LGPL - original licence link has changed is not relivant.
37248 * <script type="text/javascript">
37252 * @class Roo.bootstrap.layout.Basic
37253 * @extends Roo.util.Observable
37254 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37255 * and does not have a titlebar, tabs or any other features. All it does is size and position
37256 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37257 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37258 * @cfg {string} region the region that it inhabits..
37259 * @cfg {bool} skipConfig skip config?
37263 Roo.bootstrap.layout.Basic = function(config){
37265 this.mgr = config.mgr;
37267 this.position = config.region;
37269 var skipConfig = config.skipConfig;
37273 * @scope Roo.BasicLayoutRegion
37277 * @event beforeremove
37278 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37279 * @param {Roo.LayoutRegion} this
37280 * @param {Roo.ContentPanel} panel The panel
37281 * @param {Object} e The cancel event object
37283 "beforeremove" : true,
37285 * @event invalidated
37286 * Fires when the layout for this region is changed.
37287 * @param {Roo.LayoutRegion} this
37289 "invalidated" : true,
37291 * @event visibilitychange
37292 * Fires when this region is shown or hidden
37293 * @param {Roo.LayoutRegion} this
37294 * @param {Boolean} visibility true or false
37296 "visibilitychange" : true,
37298 * @event paneladded
37299 * Fires when a panel is added.
37300 * @param {Roo.LayoutRegion} this
37301 * @param {Roo.ContentPanel} panel The panel
37303 "paneladded" : true,
37305 * @event panelremoved
37306 * Fires when a panel is removed.
37307 * @param {Roo.LayoutRegion} this
37308 * @param {Roo.ContentPanel} panel The panel
37310 "panelremoved" : true,
37312 * @event beforecollapse
37313 * Fires when this region before collapse.
37314 * @param {Roo.LayoutRegion} this
37316 "beforecollapse" : true,
37319 * Fires when this region is collapsed.
37320 * @param {Roo.LayoutRegion} this
37322 "collapsed" : true,
37325 * Fires when this region is expanded.
37326 * @param {Roo.LayoutRegion} this
37331 * Fires when this region is slid into view.
37332 * @param {Roo.LayoutRegion} this
37334 "slideshow" : true,
37337 * Fires when this region slides out of view.
37338 * @param {Roo.LayoutRegion} this
37340 "slidehide" : true,
37342 * @event panelactivated
37343 * Fires when a panel is activated.
37344 * @param {Roo.LayoutRegion} this
37345 * @param {Roo.ContentPanel} panel The activated panel
37347 "panelactivated" : true,
37350 * Fires when the user resizes this region.
37351 * @param {Roo.LayoutRegion} this
37352 * @param {Number} newSize The new size (width for east/west, height for north/south)
37356 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37357 this.panels = new Roo.util.MixedCollection();
37358 this.panels.getKey = this.getPanelId.createDelegate(this);
37360 this.activePanel = null;
37361 // ensure listeners are added...
37363 if (config.listeners || config.events) {
37364 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37365 listeners : config.listeners || {},
37366 events : config.events || {}
37370 if(skipConfig !== true){
37371 this.applyConfig(config);
37375 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37377 getPanelId : function(p){
37381 applyConfig : function(config){
37382 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37383 this.config = config;
37388 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37389 * the width, for horizontal (north, south) the height.
37390 * @param {Number} newSize The new width or height
37392 resizeTo : function(newSize){
37393 var el = this.el ? this.el :
37394 (this.activePanel ? this.activePanel.getEl() : null);
37396 switch(this.position){
37399 el.setWidth(newSize);
37400 this.fireEvent("resized", this, newSize);
37404 el.setHeight(newSize);
37405 this.fireEvent("resized", this, newSize);
37411 getBox : function(){
37412 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37415 getMargins : function(){
37416 return this.margins;
37419 updateBox : function(box){
37421 var el = this.activePanel.getEl();
37422 el.dom.style.left = box.x + "px";
37423 el.dom.style.top = box.y + "px";
37424 this.activePanel.setSize(box.width, box.height);
37428 * Returns the container element for this region.
37429 * @return {Roo.Element}
37431 getEl : function(){
37432 return this.activePanel;
37436 * Returns true if this region is currently visible.
37437 * @return {Boolean}
37439 isVisible : function(){
37440 return this.activePanel ? true : false;
37443 setActivePanel : function(panel){
37444 panel = this.getPanel(panel);
37445 if(this.activePanel && this.activePanel != panel){
37446 this.activePanel.setActiveState(false);
37447 this.activePanel.getEl().setLeftTop(-10000,-10000);
37449 this.activePanel = panel;
37450 panel.setActiveState(true);
37452 panel.setSize(this.box.width, this.box.height);
37454 this.fireEvent("panelactivated", this, panel);
37455 this.fireEvent("invalidated");
37459 * Show the specified panel.
37460 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37461 * @return {Roo.ContentPanel} The shown panel or null
37463 showPanel : function(panel){
37464 panel = this.getPanel(panel);
37466 this.setActivePanel(panel);
37472 * Get the active panel for this region.
37473 * @return {Roo.ContentPanel} The active panel or null
37475 getActivePanel : function(){
37476 return this.activePanel;
37480 * Add the passed ContentPanel(s)
37481 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37482 * @return {Roo.ContentPanel} The panel added (if only one was added)
37484 add : function(panel){
37485 if(arguments.length > 1){
37486 for(var i = 0, len = arguments.length; i < len; i++) {
37487 this.add(arguments[i]);
37491 if(this.hasPanel(panel)){
37492 this.showPanel(panel);
37495 var el = panel.getEl();
37496 if(el.dom.parentNode != this.mgr.el.dom){
37497 this.mgr.el.dom.appendChild(el.dom);
37499 if(panel.setRegion){
37500 panel.setRegion(this);
37502 this.panels.add(panel);
37503 el.setStyle("position", "absolute");
37504 if(!panel.background){
37505 this.setActivePanel(panel);
37506 if(this.config.initialSize && this.panels.getCount()==1){
37507 this.resizeTo(this.config.initialSize);
37510 this.fireEvent("paneladded", this, panel);
37515 * Returns true if the panel is in this region.
37516 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37517 * @return {Boolean}
37519 hasPanel : function(panel){
37520 if(typeof panel == "object"){ // must be panel obj
37521 panel = panel.getId();
37523 return this.getPanel(panel) ? true : false;
37527 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37528 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37529 * @param {Boolean} preservePanel Overrides the config preservePanel option
37530 * @return {Roo.ContentPanel} The panel that was removed
37532 remove : function(panel, preservePanel){
37533 panel = this.getPanel(panel);
37538 this.fireEvent("beforeremove", this, panel, e);
37539 if(e.cancel === true){
37542 var panelId = panel.getId();
37543 this.panels.removeKey(panelId);
37548 * Returns the panel specified or null if it's not in this region.
37549 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37550 * @return {Roo.ContentPanel}
37552 getPanel : function(id){
37553 if(typeof id == "object"){ // must be panel obj
37556 return this.panels.get(id);
37560 * Returns this regions position (north/south/east/west/center).
37563 getPosition: function(){
37564 return this.position;
37568 * Ext JS Library 1.1.1
37569 * Copyright(c) 2006-2007, Ext JS, LLC.
37571 * Originally Released Under LGPL - original licence link has changed is not relivant.
37574 * <script type="text/javascript">
37578 * @class Roo.bootstrap.layout.Region
37579 * @extends Roo.bootstrap.layout.Basic
37580 * This class represents a region in a layout manager.
37582 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37583 * @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})
37584 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37585 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37586 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37587 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37588 * @cfg {String} title The title for the region (overrides panel titles)
37589 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37590 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37591 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37592 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37593 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37594 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37595 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37596 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37597 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37598 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37600 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37601 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37602 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37603 * @cfg {Number} width For East/West panels
37604 * @cfg {Number} height For North/South panels
37605 * @cfg {Boolean} split To show the splitter
37606 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37608 * @cfg {string} cls Extra CSS classes to add to region
37610 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37611 * @cfg {string} region the region that it inhabits..
37614 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37615 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37617 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37618 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37619 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37621 Roo.bootstrap.layout.Region = function(config)
37623 this.applyConfig(config);
37625 var mgr = config.mgr;
37626 var pos = config.region;
37627 config.skipConfig = true;
37628 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37631 this.onRender(mgr.el);
37634 this.visible = true;
37635 this.collapsed = false;
37636 this.unrendered_panels = [];
37639 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37641 position: '', // set by wrapper (eg. north/south etc..)
37642 unrendered_panels : null, // unrendered panels.
37644 tabPosition : false,
37646 mgr: false, // points to 'Border'
37649 createBody : function(){
37650 /** This region's body element
37651 * @type Roo.Element */
37652 this.bodyEl = this.el.createChild({
37654 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37658 onRender: function(ctr, pos)
37660 var dh = Roo.DomHelper;
37661 /** This region's container element
37662 * @type Roo.Element */
37663 this.el = dh.append(ctr.dom, {
37665 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37667 /** This region's title element
37668 * @type Roo.Element */
37670 this.titleEl = dh.append(this.el.dom, {
37672 unselectable: "on",
37673 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37675 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37676 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37680 this.titleEl.enableDisplayMode();
37681 /** This region's title text element
37682 * @type HTMLElement */
37683 this.titleTextEl = this.titleEl.dom.firstChild;
37684 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37686 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37687 this.closeBtn.enableDisplayMode();
37688 this.closeBtn.on("click", this.closeClicked, this);
37689 this.closeBtn.hide();
37691 this.createBody(this.config);
37692 if(this.config.hideWhenEmpty){
37694 this.on("paneladded", this.validateVisibility, this);
37695 this.on("panelremoved", this.validateVisibility, this);
37697 if(this.autoScroll){
37698 this.bodyEl.setStyle("overflow", "auto");
37700 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37702 //if(c.titlebar !== false){
37703 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37704 this.titleEl.hide();
37706 this.titleEl.show();
37707 if(this.config.title){
37708 this.titleTextEl.innerHTML = this.config.title;
37712 if(this.config.collapsed){
37713 this.collapse(true);
37715 if(this.config.hidden){
37719 if (this.unrendered_panels && this.unrendered_panels.length) {
37720 for (var i =0;i< this.unrendered_panels.length; i++) {
37721 this.add(this.unrendered_panels[i]);
37723 this.unrendered_panels = null;
37729 applyConfig : function(c)
37732 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37733 var dh = Roo.DomHelper;
37734 if(c.titlebar !== false){
37735 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37736 this.collapseBtn.on("click", this.collapse, this);
37737 this.collapseBtn.enableDisplayMode();
37739 if(c.showPin === true || this.showPin){
37740 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37741 this.stickBtn.enableDisplayMode();
37742 this.stickBtn.on("click", this.expand, this);
37743 this.stickBtn.hide();
37748 /** This region's collapsed element
37749 * @type Roo.Element */
37752 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37753 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37756 if(c.floatable !== false){
37757 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37758 this.collapsedEl.on("click", this.collapseClick, this);
37761 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37762 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37763 id: "message", unselectable: "on", style:{"float":"left"}});
37764 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37766 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37767 this.expandBtn.on("click", this.expand, this);
37771 if(this.collapseBtn){
37772 this.collapseBtn.setVisible(c.collapsible == true);
37775 this.cmargins = c.cmargins || this.cmargins ||
37776 (this.position == "west" || this.position == "east" ?
37777 {top: 0, left: 2, right:2, bottom: 0} :
37778 {top: 2, left: 0, right:0, bottom: 2});
37780 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37783 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37785 this.autoScroll = c.autoScroll || false;
37790 this.duration = c.duration || .30;
37791 this.slideDuration = c.slideDuration || .45;
37796 * Returns true if this region is currently visible.
37797 * @return {Boolean}
37799 isVisible : function(){
37800 return this.visible;
37804 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37805 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37807 //setCollapsedTitle : function(title){
37808 // title = title || " ";
37809 // if(this.collapsedTitleTextEl){
37810 // this.collapsedTitleTextEl.innerHTML = title;
37814 getBox : function(){
37816 // if(!this.collapsed){
37817 b = this.el.getBox(false, true);
37819 // b = this.collapsedEl.getBox(false, true);
37824 getMargins : function(){
37825 return this.margins;
37826 //return this.collapsed ? this.cmargins : this.margins;
37829 highlight : function(){
37830 this.el.addClass("x-layout-panel-dragover");
37833 unhighlight : function(){
37834 this.el.removeClass("x-layout-panel-dragover");
37837 updateBox : function(box)
37839 if (!this.bodyEl) {
37840 return; // not rendered yet..
37844 if(!this.collapsed){
37845 this.el.dom.style.left = box.x + "px";
37846 this.el.dom.style.top = box.y + "px";
37847 this.updateBody(box.width, box.height);
37849 this.collapsedEl.dom.style.left = box.x + "px";
37850 this.collapsedEl.dom.style.top = box.y + "px";
37851 this.collapsedEl.setSize(box.width, box.height);
37854 this.tabs.autoSizeTabs();
37858 updateBody : function(w, h)
37861 this.el.setWidth(w);
37862 w -= this.el.getBorderWidth("rl");
37863 if(this.config.adjustments){
37864 w += this.config.adjustments[0];
37867 if(h !== null && h > 0){
37868 this.el.setHeight(h);
37869 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37870 h -= this.el.getBorderWidth("tb");
37871 if(this.config.adjustments){
37872 h += this.config.adjustments[1];
37874 this.bodyEl.setHeight(h);
37876 h = this.tabs.syncHeight(h);
37879 if(this.panelSize){
37880 w = w !== null ? w : this.panelSize.width;
37881 h = h !== null ? h : this.panelSize.height;
37883 if(this.activePanel){
37884 var el = this.activePanel.getEl();
37885 w = w !== null ? w : el.getWidth();
37886 h = h !== null ? h : el.getHeight();
37887 this.panelSize = {width: w, height: h};
37888 this.activePanel.setSize(w, h);
37890 if(Roo.isIE && this.tabs){
37891 this.tabs.el.repaint();
37896 * Returns the container element for this region.
37897 * @return {Roo.Element}
37899 getEl : function(){
37904 * Hides this region.
37907 //if(!this.collapsed){
37908 this.el.dom.style.left = "-2000px";
37911 // this.collapsedEl.dom.style.left = "-2000px";
37912 // this.collapsedEl.hide();
37914 this.visible = false;
37915 this.fireEvent("visibilitychange", this, false);
37919 * Shows this region if it was previously hidden.
37922 //if(!this.collapsed){
37925 // this.collapsedEl.show();
37927 this.visible = true;
37928 this.fireEvent("visibilitychange", this, true);
37931 closeClicked : function(){
37932 if(this.activePanel){
37933 this.remove(this.activePanel);
37937 collapseClick : function(e){
37939 e.stopPropagation();
37942 e.stopPropagation();
37948 * Collapses this region.
37949 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37952 collapse : function(skipAnim, skipCheck = false){
37953 if(this.collapsed) {
37957 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37959 this.collapsed = true;
37961 this.split.el.hide();
37963 if(this.config.animate && skipAnim !== true){
37964 this.fireEvent("invalidated", this);
37965 this.animateCollapse();
37967 this.el.setLocation(-20000,-20000);
37969 this.collapsedEl.show();
37970 this.fireEvent("collapsed", this);
37971 this.fireEvent("invalidated", this);
37977 animateCollapse : function(){
37982 * Expands this region if it was previously collapsed.
37983 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37984 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37987 expand : function(e, skipAnim){
37989 e.stopPropagation();
37991 if(!this.collapsed || this.el.hasActiveFx()) {
37995 this.afterSlideIn();
37998 this.collapsed = false;
37999 if(this.config.animate && skipAnim !== true){
38000 this.animateExpand();
38004 this.split.el.show();
38006 this.collapsedEl.setLocation(-2000,-2000);
38007 this.collapsedEl.hide();
38008 this.fireEvent("invalidated", this);
38009 this.fireEvent("expanded", this);
38013 animateExpand : function(){
38017 initTabs : function()
38019 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38021 var ts = new Roo.bootstrap.panel.Tabs({
38022 el: this.bodyEl.dom,
38024 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38025 disableTooltips: this.config.disableTabTips,
38026 toolbar : this.config.toolbar
38029 if(this.config.hideTabs){
38030 ts.stripWrap.setDisplayed(false);
38033 ts.resizeTabs = this.config.resizeTabs === true;
38034 ts.minTabWidth = this.config.minTabWidth || 40;
38035 ts.maxTabWidth = this.config.maxTabWidth || 250;
38036 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38037 ts.monitorResize = false;
38038 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38039 ts.bodyEl.addClass('roo-layout-tabs-body');
38040 this.panels.each(this.initPanelAsTab, this);
38043 initPanelAsTab : function(panel){
38044 var ti = this.tabs.addTab(
38048 this.config.closeOnTab && panel.isClosable(),
38051 if(panel.tabTip !== undefined){
38052 ti.setTooltip(panel.tabTip);
38054 ti.on("activate", function(){
38055 this.setActivePanel(panel);
38058 if(this.config.closeOnTab){
38059 ti.on("beforeclose", function(t, e){
38061 this.remove(panel);
38065 panel.tabItem = ti;
38070 updatePanelTitle : function(panel, title)
38072 if(this.activePanel == panel){
38073 this.updateTitle(title);
38076 var ti = this.tabs.getTab(panel.getEl().id);
38078 if(panel.tabTip !== undefined){
38079 ti.setTooltip(panel.tabTip);
38084 updateTitle : function(title){
38085 if(this.titleTextEl && !this.config.title){
38086 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38090 setActivePanel : function(panel)
38092 panel = this.getPanel(panel);
38093 if(this.activePanel && this.activePanel != panel){
38094 if(this.activePanel.setActiveState(false) === false){
38098 this.activePanel = panel;
38099 panel.setActiveState(true);
38100 if(this.panelSize){
38101 panel.setSize(this.panelSize.width, this.panelSize.height);
38104 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38106 this.updateTitle(panel.getTitle());
38108 this.fireEvent("invalidated", this);
38110 this.fireEvent("panelactivated", this, panel);
38114 * Shows the specified panel.
38115 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38116 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38118 showPanel : function(panel)
38120 panel = this.getPanel(panel);
38123 var tab = this.tabs.getTab(panel.getEl().id);
38124 if(tab.isHidden()){
38125 this.tabs.unhideTab(tab.id);
38129 this.setActivePanel(panel);
38136 * Get the active panel for this region.
38137 * @return {Roo.ContentPanel} The active panel or null
38139 getActivePanel : function(){
38140 return this.activePanel;
38143 validateVisibility : function(){
38144 if(this.panels.getCount() < 1){
38145 this.updateTitle(" ");
38146 this.closeBtn.hide();
38149 if(!this.isVisible()){
38156 * Adds the passed ContentPanel(s) to this region.
38157 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38158 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38160 add : function(panel)
38162 if(arguments.length > 1){
38163 for(var i = 0, len = arguments.length; i < len; i++) {
38164 this.add(arguments[i]);
38169 // if we have not been rendered yet, then we can not really do much of this..
38170 if (!this.bodyEl) {
38171 this.unrendered_panels.push(panel);
38178 if(this.hasPanel(panel)){
38179 this.showPanel(panel);
38182 panel.setRegion(this);
38183 this.panels.add(panel);
38184 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38185 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38186 // and hide them... ???
38187 this.bodyEl.dom.appendChild(panel.getEl().dom);
38188 if(panel.background !== true){
38189 this.setActivePanel(panel);
38191 this.fireEvent("paneladded", this, panel);
38198 this.initPanelAsTab(panel);
38202 if(panel.background !== true){
38203 this.tabs.activate(panel.getEl().id);
38205 this.fireEvent("paneladded", this, panel);
38210 * Hides the tab for the specified panel.
38211 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38213 hidePanel : function(panel){
38214 if(this.tabs && (panel = this.getPanel(panel))){
38215 this.tabs.hideTab(panel.getEl().id);
38220 * Unhides the tab for a previously hidden panel.
38221 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38223 unhidePanel : function(panel){
38224 if(this.tabs && (panel = this.getPanel(panel))){
38225 this.tabs.unhideTab(panel.getEl().id);
38229 clearPanels : function(){
38230 while(this.panels.getCount() > 0){
38231 this.remove(this.panels.first());
38236 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38237 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38238 * @param {Boolean} preservePanel Overrides the config preservePanel option
38239 * @return {Roo.ContentPanel} The panel that was removed
38241 remove : function(panel, preservePanel)
38243 panel = this.getPanel(panel);
38248 this.fireEvent("beforeremove", this, panel, e);
38249 if(e.cancel === true){
38252 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38253 var panelId = panel.getId();
38254 this.panels.removeKey(panelId);
38256 document.body.appendChild(panel.getEl().dom);
38259 this.tabs.removeTab(panel.getEl().id);
38260 }else if (!preservePanel){
38261 this.bodyEl.dom.removeChild(panel.getEl().dom);
38263 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38264 var p = this.panels.first();
38265 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38266 tempEl.appendChild(p.getEl().dom);
38267 this.bodyEl.update("");
38268 this.bodyEl.dom.appendChild(p.getEl().dom);
38270 this.updateTitle(p.getTitle());
38272 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38273 this.setActivePanel(p);
38275 panel.setRegion(null);
38276 if(this.activePanel == panel){
38277 this.activePanel = null;
38279 if(this.config.autoDestroy !== false && preservePanel !== true){
38280 try{panel.destroy();}catch(e){}
38282 this.fireEvent("panelremoved", this, panel);
38287 * Returns the TabPanel component used by this region
38288 * @return {Roo.TabPanel}
38290 getTabs : function(){
38294 createTool : function(parentEl, className){
38295 var btn = Roo.DomHelper.append(parentEl, {
38297 cls: "x-layout-tools-button",
38300 cls: "roo-layout-tools-button-inner " + className,
38304 btn.addClassOnOver("roo-layout-tools-button-over");
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">
38321 * @class Roo.SplitLayoutRegion
38322 * @extends Roo.LayoutRegion
38323 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38325 Roo.bootstrap.layout.Split = function(config){
38326 this.cursor = config.cursor;
38327 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38330 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38332 splitTip : "Drag to resize.",
38333 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38334 useSplitTips : false,
38336 applyConfig : function(config){
38337 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38340 onRender : function(ctr,pos) {
38342 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38343 if(!this.config.split){
38348 var splitEl = Roo.DomHelper.append(ctr.dom, {
38350 id: this.el.id + "-split",
38351 cls: "roo-layout-split roo-layout-split-"+this.position,
38354 /** The SplitBar for this region
38355 * @type Roo.SplitBar */
38356 // does not exist yet...
38357 Roo.log([this.position, this.orientation]);
38359 this.split = new Roo.bootstrap.SplitBar({
38360 dragElement : splitEl,
38361 resizingElement: this.el,
38362 orientation : this.orientation
38365 this.split.on("moved", this.onSplitMove, this);
38366 this.split.useShim = this.config.useShim === true;
38367 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38368 if(this.useSplitTips){
38369 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38371 //if(config.collapsible){
38372 // this.split.el.on("dblclick", this.collapse, this);
38375 if(typeof this.config.minSize != "undefined"){
38376 this.split.minSize = this.config.minSize;
38378 if(typeof this.config.maxSize != "undefined"){
38379 this.split.maxSize = this.config.maxSize;
38381 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38382 this.hideSplitter();
38387 getHMaxSize : function(){
38388 var cmax = this.config.maxSize || 10000;
38389 var center = this.mgr.getRegion("center");
38390 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38393 getVMaxSize : function(){
38394 var cmax = this.config.maxSize || 10000;
38395 var center = this.mgr.getRegion("center");
38396 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38399 onSplitMove : function(split, newSize){
38400 this.fireEvent("resized", this, newSize);
38404 * Returns the {@link Roo.SplitBar} for this region.
38405 * @return {Roo.SplitBar}
38407 getSplitBar : function(){
38412 this.hideSplitter();
38413 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38416 hideSplitter : function(){
38418 this.split.el.setLocation(-2000,-2000);
38419 this.split.el.hide();
38425 this.split.el.show();
38427 Roo.bootstrap.layout.Split.superclass.show.call(this);
38430 beforeSlide: function(){
38431 if(Roo.isGecko){// firefox overflow auto bug workaround
38432 this.bodyEl.clip();
38434 this.tabs.bodyEl.clip();
38436 if(this.activePanel){
38437 this.activePanel.getEl().clip();
38439 if(this.activePanel.beforeSlide){
38440 this.activePanel.beforeSlide();
38446 afterSlide : function(){
38447 if(Roo.isGecko){// firefox overflow auto bug workaround
38448 this.bodyEl.unclip();
38450 this.tabs.bodyEl.unclip();
38452 if(this.activePanel){
38453 this.activePanel.getEl().unclip();
38454 if(this.activePanel.afterSlide){
38455 this.activePanel.afterSlide();
38461 initAutoHide : function(){
38462 if(this.autoHide !== false){
38463 if(!this.autoHideHd){
38464 var st = new Roo.util.DelayedTask(this.slideIn, this);
38465 this.autoHideHd = {
38466 "mouseout": function(e){
38467 if(!e.within(this.el, true)){
38471 "mouseover" : function(e){
38477 this.el.on(this.autoHideHd);
38481 clearAutoHide : function(){
38482 if(this.autoHide !== false){
38483 this.el.un("mouseout", this.autoHideHd.mouseout);
38484 this.el.un("mouseover", this.autoHideHd.mouseover);
38488 clearMonitor : function(){
38489 Roo.get(document).un("click", this.slideInIf, this);
38492 // these names are backwards but not changed for compat
38493 slideOut : function(){
38494 if(this.isSlid || this.el.hasActiveFx()){
38497 this.isSlid = true;
38498 if(this.collapseBtn){
38499 this.collapseBtn.hide();
38501 this.closeBtnState = this.closeBtn.getStyle('display');
38502 this.closeBtn.hide();
38504 this.stickBtn.show();
38507 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38508 this.beforeSlide();
38509 this.el.setStyle("z-index", 10001);
38510 this.el.slideIn(this.getSlideAnchor(), {
38511 callback: function(){
38513 this.initAutoHide();
38514 Roo.get(document).on("click", this.slideInIf, this);
38515 this.fireEvent("slideshow", this);
38522 afterSlideIn : function(){
38523 this.clearAutoHide();
38524 this.isSlid = false;
38525 this.clearMonitor();
38526 this.el.setStyle("z-index", "");
38527 if(this.collapseBtn){
38528 this.collapseBtn.show();
38530 this.closeBtn.setStyle('display', this.closeBtnState);
38532 this.stickBtn.hide();
38534 this.fireEvent("slidehide", this);
38537 slideIn : function(cb){
38538 if(!this.isSlid || this.el.hasActiveFx()){
38542 this.isSlid = false;
38543 this.beforeSlide();
38544 this.el.slideOut(this.getSlideAnchor(), {
38545 callback: function(){
38546 this.el.setLeftTop(-10000, -10000);
38548 this.afterSlideIn();
38556 slideInIf : function(e){
38557 if(!e.within(this.el)){
38562 animateCollapse : function(){
38563 this.beforeSlide();
38564 this.el.setStyle("z-index", 20000);
38565 var anchor = this.getSlideAnchor();
38566 this.el.slideOut(anchor, {
38567 callback : function(){
38568 this.el.setStyle("z-index", "");
38569 this.collapsedEl.slideIn(anchor, {duration:.3});
38571 this.el.setLocation(-10000,-10000);
38573 this.fireEvent("collapsed", this);
38580 animateExpand : function(){
38581 this.beforeSlide();
38582 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38583 this.el.setStyle("z-index", 20000);
38584 this.collapsedEl.hide({
38587 this.el.slideIn(this.getSlideAnchor(), {
38588 callback : function(){
38589 this.el.setStyle("z-index", "");
38592 this.split.el.show();
38594 this.fireEvent("invalidated", this);
38595 this.fireEvent("expanded", this);
38623 getAnchor : function(){
38624 return this.anchors[this.position];
38627 getCollapseAnchor : function(){
38628 return this.canchors[this.position];
38631 getSlideAnchor : function(){
38632 return this.sanchors[this.position];
38635 getAlignAdj : function(){
38636 var cm = this.cmargins;
38637 switch(this.position){
38653 getExpandAdj : function(){
38654 var c = this.collapsedEl, cm = this.cmargins;
38655 switch(this.position){
38657 return [-(cm.right+c.getWidth()+cm.left), 0];
38660 return [cm.right+c.getWidth()+cm.left, 0];
38663 return [0, -(cm.top+cm.bottom+c.getHeight())];
38666 return [0, cm.top+cm.bottom+c.getHeight()];
38672 * Ext JS Library 1.1.1
38673 * Copyright(c) 2006-2007, Ext JS, LLC.
38675 * Originally Released Under LGPL - original licence link has changed is not relivant.
38678 * <script type="text/javascript">
38681 * These classes are private internal classes
38683 Roo.bootstrap.layout.Center = function(config){
38684 config.region = "center";
38685 Roo.bootstrap.layout.Region.call(this, config);
38686 this.visible = true;
38687 this.minWidth = config.minWidth || 20;
38688 this.minHeight = config.minHeight || 20;
38691 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38693 // center panel can't be hidden
38697 // center panel can't be hidden
38700 getMinWidth: function(){
38701 return this.minWidth;
38704 getMinHeight: function(){
38705 return this.minHeight;
38719 Roo.bootstrap.layout.North = function(config)
38721 config.region = 'north';
38722 config.cursor = 'n-resize';
38724 Roo.bootstrap.layout.Split.call(this, config);
38728 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38729 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38730 this.split.el.addClass("roo-layout-split-v");
38732 var size = config.initialSize || config.height;
38733 if(typeof size != "undefined"){
38734 this.el.setHeight(size);
38737 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38739 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38743 getBox : function(){
38744 if(this.collapsed){
38745 return this.collapsedEl.getBox();
38747 var box = this.el.getBox();
38749 box.height += this.split.el.getHeight();
38754 updateBox : function(box){
38755 if(this.split && !this.collapsed){
38756 box.height -= this.split.el.getHeight();
38757 this.split.el.setLeft(box.x);
38758 this.split.el.setTop(box.y+box.height);
38759 this.split.el.setWidth(box.width);
38761 if(this.collapsed){
38762 this.updateBody(box.width, null);
38764 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38772 Roo.bootstrap.layout.South = function(config){
38773 config.region = 'south';
38774 config.cursor = 's-resize';
38775 Roo.bootstrap.layout.Split.call(this, config);
38777 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38778 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38779 this.split.el.addClass("roo-layout-split-v");
38781 var size = config.initialSize || config.height;
38782 if(typeof size != "undefined"){
38783 this.el.setHeight(size);
38787 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38788 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38789 getBox : function(){
38790 if(this.collapsed){
38791 return this.collapsedEl.getBox();
38793 var box = this.el.getBox();
38795 var sh = this.split.el.getHeight();
38802 updateBox : function(box){
38803 if(this.split && !this.collapsed){
38804 var sh = this.split.el.getHeight();
38807 this.split.el.setLeft(box.x);
38808 this.split.el.setTop(box.y-sh);
38809 this.split.el.setWidth(box.width);
38811 if(this.collapsed){
38812 this.updateBody(box.width, null);
38814 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38818 Roo.bootstrap.layout.East = function(config){
38819 config.region = "east";
38820 config.cursor = "e-resize";
38821 Roo.bootstrap.layout.Split.call(this, config);
38823 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38824 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38825 this.split.el.addClass("roo-layout-split-h");
38827 var size = config.initialSize || config.width;
38828 if(typeof size != "undefined"){
38829 this.el.setWidth(size);
38832 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38833 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38834 getBox : function(){
38835 if(this.collapsed){
38836 return this.collapsedEl.getBox();
38838 var box = this.el.getBox();
38840 var sw = this.split.el.getWidth();
38847 updateBox : function(box){
38848 if(this.split && !this.collapsed){
38849 var sw = this.split.el.getWidth();
38851 this.split.el.setLeft(box.x);
38852 this.split.el.setTop(box.y);
38853 this.split.el.setHeight(box.height);
38856 if(this.collapsed){
38857 this.updateBody(null, box.height);
38859 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38863 Roo.bootstrap.layout.West = function(config){
38864 config.region = "west";
38865 config.cursor = "w-resize";
38867 Roo.bootstrap.layout.Split.call(this, config);
38869 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38870 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38871 this.split.el.addClass("roo-layout-split-h");
38875 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38876 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38878 onRender: function(ctr, pos)
38880 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38881 var size = this.config.initialSize || this.config.width;
38882 if(typeof size != "undefined"){
38883 this.el.setWidth(size);
38887 getBox : function(){
38888 if(this.collapsed){
38889 return this.collapsedEl.getBox();
38891 var box = this.el.getBox();
38893 box.width += this.split.el.getWidth();
38898 updateBox : function(box){
38899 if(this.split && !this.collapsed){
38900 var sw = this.split.el.getWidth();
38902 this.split.el.setLeft(box.x+box.width);
38903 this.split.el.setTop(box.y);
38904 this.split.el.setHeight(box.height);
38906 if(this.collapsed){
38907 this.updateBody(null, box.height);
38909 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38911 });Roo.namespace("Roo.bootstrap.panel");/*
38913 * Ext JS Library 1.1.1
38914 * Copyright(c) 2006-2007, Ext JS, LLC.
38916 * Originally Released Under LGPL - original licence link has changed is not relivant.
38919 * <script type="text/javascript">
38922 * @class Roo.ContentPanel
38923 * @extends Roo.util.Observable
38924 * A basic ContentPanel element.
38925 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38926 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38927 * @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
38928 * @cfg {Boolean} closable True if the panel can be closed/removed
38929 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38930 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38931 * @cfg {Toolbar} toolbar A toolbar for this panel
38932 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38933 * @cfg {String} title The title for this panel
38934 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38935 * @cfg {String} url Calls {@link #setUrl} with this value
38936 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38937 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38938 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38939 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38940 * @cfg {Boolean} badges render the badges
38943 * Create a new ContentPanel.
38944 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38945 * @param {String/Object} config A string to set only the title or a config object
38946 * @param {String} content (optional) Set the HTML content for this panel
38947 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38949 Roo.bootstrap.panel.Content = function( config){
38951 this.tpl = config.tpl || false;
38953 var el = config.el;
38954 var content = config.content;
38956 if(config.autoCreate){ // xtype is available if this is called from factory
38959 this.el = Roo.get(el);
38960 if(!this.el && config && config.autoCreate){
38961 if(typeof config.autoCreate == "object"){
38962 if(!config.autoCreate.id){
38963 config.autoCreate.id = config.id||el;
38965 this.el = Roo.DomHelper.append(document.body,
38966 config.autoCreate, true);
38968 var elcfg = { tag: "div",
38969 cls: "roo-layout-inactive-content",
38973 elcfg.html = config.html;
38977 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38980 this.closable = false;
38981 this.loaded = false;
38982 this.active = false;
38985 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38987 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38989 this.wrapEl = this.el; //this.el.wrap();
38991 if (config.toolbar.items) {
38992 ti = config.toolbar.items ;
38993 delete config.toolbar.items ;
38997 this.toolbar.render(this.wrapEl, 'before');
38998 for(var i =0;i < ti.length;i++) {
38999 // Roo.log(['add child', items[i]]);
39000 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39002 this.toolbar.items = nitems;
39003 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39004 delete config.toolbar;
39008 // xtype created footer. - not sure if will work as we normally have to render first..
39009 if (this.footer && !this.footer.el && this.footer.xtype) {
39010 if (!this.wrapEl) {
39011 this.wrapEl = this.el.wrap();
39014 this.footer.container = this.wrapEl.createChild();
39016 this.footer = Roo.factory(this.footer, Roo);
39021 if(typeof config == "string"){
39022 this.title = config;
39024 Roo.apply(this, config);
39028 this.resizeEl = Roo.get(this.resizeEl, true);
39030 this.resizeEl = this.el;
39032 // handle view.xtype
39040 * Fires when this panel is activated.
39041 * @param {Roo.ContentPanel} this
39045 * @event deactivate
39046 * Fires when this panel is activated.
39047 * @param {Roo.ContentPanel} this
39049 "deactivate" : true,
39053 * Fires when this panel is resized if fitToFrame is true.
39054 * @param {Roo.ContentPanel} this
39055 * @param {Number} width The width after any component adjustments
39056 * @param {Number} height The height after any component adjustments
39062 * Fires when this tab is created
39063 * @param {Roo.ContentPanel} this
39074 if(this.autoScroll){
39075 this.resizeEl.setStyle("overflow", "auto");
39077 // fix randome scrolling
39078 //this.el.on('scroll', function() {
39079 // Roo.log('fix random scolling');
39080 // this.scrollTo('top',0);
39083 content = content || this.content;
39085 this.setContent(content);
39087 if(config && config.url){
39088 this.setUrl(this.url, this.params, this.loadOnce);
39093 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39095 if (this.view && typeof(this.view.xtype) != 'undefined') {
39096 this.view.el = this.el.appendChild(document.createElement("div"));
39097 this.view = Roo.factory(this.view);
39098 this.view.render && this.view.render(false, '');
39102 this.fireEvent('render', this);
39105 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39109 setRegion : function(region){
39110 this.region = region;
39111 this.setActiveClass(region && !this.background);
39115 setActiveClass: function(state)
39118 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39119 this.el.setStyle('position','relative');
39121 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39122 this.el.setStyle('position', 'absolute');
39127 * Returns the toolbar for this Panel if one was configured.
39128 * @return {Roo.Toolbar}
39130 getToolbar : function(){
39131 return this.toolbar;
39134 setActiveState : function(active)
39136 this.active = active;
39137 this.setActiveClass(active);
39139 if(this.fireEvent("deactivate", this) === false){
39144 this.fireEvent("activate", this);
39148 * Updates this panel's element
39149 * @param {String} content The new content
39150 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39152 setContent : function(content, loadScripts){
39153 this.el.update(content, loadScripts);
39156 ignoreResize : function(w, h){
39157 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39160 this.lastSize = {width: w, height: h};
39165 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39166 * @return {Roo.UpdateManager} The UpdateManager
39168 getUpdateManager : function(){
39169 return this.el.getUpdateManager();
39172 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39173 * @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:
39176 url: "your-url.php",
39177 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39178 callback: yourFunction,
39179 scope: yourObject, //(optional scope)
39182 text: "Loading...",
39187 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39188 * 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.
39189 * @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}
39190 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39191 * @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.
39192 * @return {Roo.ContentPanel} this
39195 var um = this.el.getUpdateManager();
39196 um.update.apply(um, arguments);
39202 * 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.
39203 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39204 * @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)
39205 * @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)
39206 * @return {Roo.UpdateManager} The UpdateManager
39208 setUrl : function(url, params, loadOnce){
39209 if(this.refreshDelegate){
39210 this.removeListener("activate", this.refreshDelegate);
39212 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39213 this.on("activate", this.refreshDelegate);
39214 return this.el.getUpdateManager();
39217 _handleRefresh : function(url, params, loadOnce){
39218 if(!loadOnce || !this.loaded){
39219 var updater = this.el.getUpdateManager();
39220 updater.update(url, params, this._setLoaded.createDelegate(this));
39224 _setLoaded : function(){
39225 this.loaded = true;
39229 * Returns this panel's id
39232 getId : function(){
39237 * Returns this panel's element - used by regiosn to add.
39238 * @return {Roo.Element}
39240 getEl : function(){
39241 return this.wrapEl || this.el;
39246 adjustForComponents : function(width, height)
39248 //Roo.log('adjustForComponents ');
39249 if(this.resizeEl != this.el){
39250 width -= this.el.getFrameWidth('lr');
39251 height -= this.el.getFrameWidth('tb');
39254 var te = this.toolbar.getEl();
39255 te.setWidth(width);
39256 height -= te.getHeight();
39259 var te = this.footer.getEl();
39260 te.setWidth(width);
39261 height -= te.getHeight();
39265 if(this.adjustments){
39266 width += this.adjustments[0];
39267 height += this.adjustments[1];
39269 return {"width": width, "height": height};
39272 setSize : function(width, height){
39273 if(this.fitToFrame && !this.ignoreResize(width, height)){
39274 if(this.fitContainer && this.resizeEl != this.el){
39275 this.el.setSize(width, height);
39277 var size = this.adjustForComponents(width, height);
39278 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39279 this.fireEvent('resize', this, size.width, size.height);
39284 * Returns this panel's title
39287 getTitle : function(){
39289 if (typeof(this.title) != 'object') {
39294 for (var k in this.title) {
39295 if (!this.title.hasOwnProperty(k)) {
39299 if (k.indexOf('-') >= 0) {
39300 var s = k.split('-');
39301 for (var i = 0; i<s.length; i++) {
39302 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39305 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39312 * Set this panel's title
39313 * @param {String} title
39315 setTitle : function(title){
39316 this.title = title;
39318 this.region.updatePanelTitle(this, title);
39323 * Returns true is this panel was configured to be closable
39324 * @return {Boolean}
39326 isClosable : function(){
39327 return this.closable;
39330 beforeSlide : function(){
39332 this.resizeEl.clip();
39335 afterSlide : function(){
39337 this.resizeEl.unclip();
39341 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39342 * Will fail silently if the {@link #setUrl} method has not been called.
39343 * This does not activate the panel, just updates its content.
39345 refresh : function(){
39346 if(this.refreshDelegate){
39347 this.loaded = false;
39348 this.refreshDelegate();
39353 * Destroys this panel
39355 destroy : function(){
39356 this.el.removeAllListeners();
39357 var tempEl = document.createElement("span");
39358 tempEl.appendChild(this.el.dom);
39359 tempEl.innerHTML = "";
39365 * form - if the content panel contains a form - this is a reference to it.
39366 * @type {Roo.form.Form}
39370 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39371 * This contains a reference to it.
39377 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39387 * @param {Object} cfg Xtype definition of item to add.
39391 getChildContainer: function () {
39392 return this.getEl();
39397 var ret = new Roo.factory(cfg);
39402 if (cfg.xtype.match(/^Form$/)) {
39405 //if (this.footer) {
39406 // el = this.footer.container.insertSibling(false, 'before');
39408 el = this.el.createChild();
39411 this.form = new Roo.form.Form(cfg);
39414 if ( this.form.allItems.length) {
39415 this.form.render(el.dom);
39419 // should only have one of theses..
39420 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39421 // views.. should not be just added - used named prop 'view''
39423 cfg.el = this.el.appendChild(document.createElement("div"));
39426 var ret = new Roo.factory(cfg);
39428 ret.render && ret.render(false, ''); // render blank..
39438 * @class Roo.bootstrap.panel.Grid
39439 * @extends Roo.bootstrap.panel.Content
39441 * Create a new GridPanel.
39442 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39443 * @param {Object} config A the config object
39449 Roo.bootstrap.panel.Grid = function(config)
39453 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39454 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39456 config.el = this.wrapper;
39457 //this.el = this.wrapper;
39459 if (config.container) {
39460 // ctor'ed from a Border/panel.grid
39463 this.wrapper.setStyle("overflow", "hidden");
39464 this.wrapper.addClass('roo-grid-container');
39469 if(config.toolbar){
39470 var tool_el = this.wrapper.createChild();
39471 this.toolbar = Roo.factory(config.toolbar);
39473 if (config.toolbar.items) {
39474 ti = config.toolbar.items ;
39475 delete config.toolbar.items ;
39479 this.toolbar.render(tool_el);
39480 for(var i =0;i < ti.length;i++) {
39481 // Roo.log(['add child', items[i]]);
39482 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39484 this.toolbar.items = nitems;
39486 delete config.toolbar;
39489 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39490 config.grid.scrollBody = true;;
39491 config.grid.monitorWindowResize = false; // turn off autosizing
39492 config.grid.autoHeight = false;
39493 config.grid.autoWidth = false;
39495 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39497 if (config.background) {
39498 // render grid on panel activation (if panel background)
39499 this.on('activate', function(gp) {
39500 if (!gp.grid.rendered) {
39501 gp.grid.render(this.wrapper);
39502 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39507 this.grid.render(this.wrapper);
39508 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39511 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39512 // ??? needed ??? config.el = this.wrapper;
39517 // xtype created footer. - not sure if will work as we normally have to render first..
39518 if (this.footer && !this.footer.el && this.footer.xtype) {
39520 var ctr = this.grid.getView().getFooterPanel(true);
39521 this.footer.dataSource = this.grid.dataSource;
39522 this.footer = Roo.factory(this.footer, Roo);
39523 this.footer.render(ctr);
39533 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39534 getId : function(){
39535 return this.grid.id;
39539 * Returns the grid for this panel
39540 * @return {Roo.bootstrap.Table}
39542 getGrid : function(){
39546 setSize : function(width, height){
39547 if(!this.ignoreResize(width, height)){
39548 var grid = this.grid;
39549 var size = this.adjustForComponents(width, height);
39550 var gridel = grid.getGridEl();
39551 gridel.setSize(size.width, size.height);
39553 var thd = grid.getGridEl().select('thead',true).first();
39554 var tbd = grid.getGridEl().select('tbody', true).first();
39556 tbd.setSize(width, height - thd.getHeight());
39565 beforeSlide : function(){
39566 this.grid.getView().scroller.clip();
39569 afterSlide : function(){
39570 this.grid.getView().scroller.unclip();
39573 destroy : function(){
39574 this.grid.destroy();
39576 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39581 * @class Roo.bootstrap.panel.Nest
39582 * @extends Roo.bootstrap.panel.Content
39584 * Create a new Panel, that can contain a layout.Border.
39587 * @param {Roo.BorderLayout} layout The layout for this panel
39588 * @param {String/Object} config A string to set only the title or a config object
39590 Roo.bootstrap.panel.Nest = function(config)
39592 // construct with only one argument..
39593 /* FIXME - implement nicer consturctors
39594 if (layout.layout) {
39596 layout = config.layout;
39597 delete config.layout;
39599 if (layout.xtype && !layout.getEl) {
39600 // then layout needs constructing..
39601 layout = Roo.factory(layout, Roo);
39605 config.el = config.layout.getEl();
39607 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39609 config.layout.monitorWindowResize = false; // turn off autosizing
39610 this.layout = config.layout;
39611 this.layout.getEl().addClass("roo-layout-nested-layout");
39612 this.layout.parent = this;
39619 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39621 setSize : function(width, height){
39622 if(!this.ignoreResize(width, height)){
39623 var size = this.adjustForComponents(width, height);
39624 var el = this.layout.getEl();
39625 if (size.height < 1) {
39626 el.setWidth(size.width);
39628 el.setSize(size.width, size.height);
39630 var touch = el.dom.offsetWidth;
39631 this.layout.layout();
39632 // ie requires a double layout on the first pass
39633 if(Roo.isIE && !this.initialized){
39634 this.initialized = true;
39635 this.layout.layout();
39640 // activate all subpanels if not currently active..
39642 setActiveState : function(active){
39643 this.active = active;
39644 this.setActiveClass(active);
39647 this.fireEvent("deactivate", this);
39651 this.fireEvent("activate", this);
39652 // not sure if this should happen before or after..
39653 if (!this.layout) {
39654 return; // should not happen..
39657 for (var r in this.layout.regions) {
39658 reg = this.layout.getRegion(r);
39659 if (reg.getActivePanel()) {
39660 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39661 reg.setActivePanel(reg.getActivePanel());
39664 if (!reg.panels.length) {
39667 reg.showPanel(reg.getPanel(0));
39676 * Returns the nested BorderLayout for this panel
39677 * @return {Roo.BorderLayout}
39679 getLayout : function(){
39680 return this.layout;
39684 * Adds a xtype elements to the layout of the nested panel
39688 xtype : 'ContentPanel',
39695 xtype : 'NestedLayoutPanel',
39701 items : [ ... list of content panels or nested layout panels.. ]
39705 * @param {Object} cfg Xtype definition of item to add.
39707 addxtype : function(cfg) {
39708 return this.layout.addxtype(cfg);
39713 * Ext JS Library 1.1.1
39714 * Copyright(c) 2006-2007, Ext JS, LLC.
39716 * Originally Released Under LGPL - original licence link has changed is not relivant.
39719 * <script type="text/javascript">
39722 * @class Roo.TabPanel
39723 * @extends Roo.util.Observable
39724 * A lightweight tab container.
39728 // basic tabs 1, built from existing content
39729 var tabs = new Roo.TabPanel("tabs1");
39730 tabs.addTab("script", "View Script");
39731 tabs.addTab("markup", "View Markup");
39732 tabs.activate("script");
39734 // more advanced tabs, built from javascript
39735 var jtabs = new Roo.TabPanel("jtabs");
39736 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39738 // set up the UpdateManager
39739 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39740 var updater = tab2.getUpdateManager();
39741 updater.setDefaultUrl("ajax1.htm");
39742 tab2.on('activate', updater.refresh, updater, true);
39744 // Use setUrl for Ajax loading
39745 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39746 tab3.setUrl("ajax2.htm", null, true);
39749 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39752 jtabs.activate("jtabs-1");
39755 * Create a new TabPanel.
39756 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39757 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39759 Roo.bootstrap.panel.Tabs = function(config){
39761 * The container element for this TabPanel.
39762 * @type Roo.Element
39764 this.el = Roo.get(config.el);
39767 if(typeof config == "boolean"){
39768 this.tabPosition = config ? "bottom" : "top";
39770 Roo.apply(this, config);
39774 if(this.tabPosition == "bottom"){
39775 // if tabs are at the bottom = create the body first.
39776 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39777 this.el.addClass("roo-tabs-bottom");
39779 // next create the tabs holders
39781 if (this.tabPosition == "west"){
39783 var reg = this.region; // fake it..
39785 if (!reg.mgr.parent) {
39788 reg = reg.mgr.parent.region;
39790 Roo.log("got nest?");
39792 if (reg.mgr.getRegion('west')) {
39793 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39794 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39795 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39796 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39797 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39805 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39806 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39807 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39808 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39813 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39816 // finally - if tabs are at the top, then create the body last..
39817 if(this.tabPosition != "bottom"){
39818 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39819 * @type Roo.Element
39821 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39822 this.el.addClass("roo-tabs-top");
39826 this.bodyEl.setStyle("position", "relative");
39828 this.active = null;
39829 this.activateDelegate = this.activate.createDelegate(this);
39834 * Fires when the active tab changes
39835 * @param {Roo.TabPanel} this
39836 * @param {Roo.TabPanelItem} activePanel The new active tab
39840 * @event beforetabchange
39841 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39842 * @param {Roo.TabPanel} this
39843 * @param {Object} e Set cancel to true on this object to cancel the tab change
39844 * @param {Roo.TabPanelItem} tab The tab being changed to
39846 "beforetabchange" : true
39849 Roo.EventManager.onWindowResize(this.onResize, this);
39850 this.cpad = this.el.getPadding("lr");
39851 this.hiddenCount = 0;
39854 // toolbar on the tabbar support...
39855 if (this.toolbar) {
39856 alert("no toolbar support yet");
39857 this.toolbar = false;
39859 var tcfg = this.toolbar;
39860 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39861 this.toolbar = new Roo.Toolbar(tcfg);
39862 if (Roo.isSafari) {
39863 var tbl = tcfg.container.child('table', true);
39864 tbl.setAttribute('width', '100%');
39872 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39875 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39877 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39879 tabPosition : "top",
39881 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39883 currentTabWidth : 0,
39885 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39889 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39893 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39895 preferredTabWidth : 175,
39897 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39899 resizeTabs : false,
39901 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39903 monitorResize : true,
39905 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39907 toolbar : false, // set by caller..
39909 region : false, /// set by caller
39911 disableTooltips : true, // not used yet...
39914 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39915 * @param {String} id The id of the div to use <b>or create</b>
39916 * @param {String} text The text for the tab
39917 * @param {String} content (optional) Content to put in the TabPanelItem body
39918 * @param {Boolean} closable (optional) True to create a close icon on the tab
39919 * @return {Roo.TabPanelItem} The created TabPanelItem
39921 addTab : function(id, text, content, closable, tpl)
39923 var item = new Roo.bootstrap.panel.TabItem({
39927 closable : closable,
39930 this.addTabItem(item);
39932 item.setContent(content);
39938 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39939 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39940 * @return {Roo.TabPanelItem}
39942 getTab : function(id){
39943 return this.items[id];
39947 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39948 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39950 hideTab : function(id){
39951 var t = this.items[id];
39954 this.hiddenCount++;
39955 this.autoSizeTabs();
39960 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39961 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39963 unhideTab : function(id){
39964 var t = this.items[id];
39966 t.setHidden(false);
39967 this.hiddenCount--;
39968 this.autoSizeTabs();
39973 * Adds an existing {@link Roo.TabPanelItem}.
39974 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39976 addTabItem : function(item)
39978 this.items[item.id] = item;
39979 this.items.push(item);
39980 this.autoSizeTabs();
39981 // if(this.resizeTabs){
39982 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39983 // this.autoSizeTabs();
39985 // item.autoSize();
39990 * Removes a {@link Roo.TabPanelItem}.
39991 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39993 removeTab : function(id){
39994 var items = this.items;
39995 var tab = items[id];
39996 if(!tab) { return; }
39997 var index = items.indexOf(tab);
39998 if(this.active == tab && items.length > 1){
39999 var newTab = this.getNextAvailable(index);
40004 this.stripEl.dom.removeChild(tab.pnode.dom);
40005 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40006 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40008 items.splice(index, 1);
40009 delete this.items[tab.id];
40010 tab.fireEvent("close", tab);
40011 tab.purgeListeners();
40012 this.autoSizeTabs();
40015 getNextAvailable : function(start){
40016 var items = this.items;
40018 // look for a next tab that will slide over to
40019 // replace the one being removed
40020 while(index < items.length){
40021 var item = items[++index];
40022 if(item && !item.isHidden()){
40026 // if one isn't found select the previous tab (on the left)
40029 var item = items[--index];
40030 if(item && !item.isHidden()){
40038 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40039 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40041 disableTab : function(id){
40042 var tab = this.items[id];
40043 if(tab && this.active != tab){
40049 * Enables a {@link Roo.TabPanelItem} that is disabled.
40050 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40052 enableTab : function(id){
40053 var tab = this.items[id];
40058 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40059 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40060 * @return {Roo.TabPanelItem} The TabPanelItem.
40062 activate : function(id)
40064 //Roo.log('activite:' + id);
40066 var tab = this.items[id];
40070 if(tab == this.active || tab.disabled){
40074 this.fireEvent("beforetabchange", this, e, tab);
40075 if(e.cancel !== true && !tab.disabled){
40077 this.active.hide();
40079 this.active = this.items[id];
40080 this.active.show();
40081 this.fireEvent("tabchange", this, this.active);
40087 * Gets the active {@link Roo.TabPanelItem}.
40088 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40090 getActiveTab : function(){
40091 return this.active;
40095 * Updates the tab body element to fit the height of the container element
40096 * for overflow scrolling
40097 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40099 syncHeight : function(targetHeight){
40100 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40101 var bm = this.bodyEl.getMargins();
40102 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40103 this.bodyEl.setHeight(newHeight);
40107 onResize : function(){
40108 if(this.monitorResize){
40109 this.autoSizeTabs();
40114 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40116 beginUpdate : function(){
40117 this.updating = true;
40121 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40123 endUpdate : function(){
40124 this.updating = false;
40125 this.autoSizeTabs();
40129 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40131 autoSizeTabs : function()
40133 var count = this.items.length;
40134 var vcount = count - this.hiddenCount;
40137 this.stripEl.hide();
40139 this.stripEl.show();
40142 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40147 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40148 var availWidth = Math.floor(w / vcount);
40149 var b = this.stripBody;
40150 if(b.getWidth() > w){
40151 var tabs = this.items;
40152 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40153 if(availWidth < this.minTabWidth){
40154 /*if(!this.sleft){ // incomplete scrolling code
40155 this.createScrollButtons();
40158 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40161 if(this.currentTabWidth < this.preferredTabWidth){
40162 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40168 * Returns the number of tabs in this TabPanel.
40171 getCount : function(){
40172 return this.items.length;
40176 * Resizes all the tabs to the passed width
40177 * @param {Number} The new width
40179 setTabWidth : function(width){
40180 this.currentTabWidth = width;
40181 for(var i = 0, len = this.items.length; i < len; i++) {
40182 if(!this.items[i].isHidden()) {
40183 this.items[i].setWidth(width);
40189 * Destroys this TabPanel
40190 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40192 destroy : function(removeEl){
40193 Roo.EventManager.removeResizeListener(this.onResize, this);
40194 for(var i = 0, len = this.items.length; i < len; i++){
40195 this.items[i].purgeListeners();
40197 if(removeEl === true){
40198 this.el.update("");
40203 createStrip : function(container)
40205 var strip = document.createElement("nav");
40206 strip.className = Roo.bootstrap.version == 4 ?
40207 "navbar-light bg-light" :
40208 "navbar navbar-default"; //"x-tabs-wrap";
40209 container.appendChild(strip);
40213 createStripList : function(strip)
40215 // div wrapper for retard IE
40216 // returns the "tr" element.
40217 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40218 //'<div class="x-tabs-strip-wrap">'+
40219 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40220 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40221 return strip.firstChild; //.firstChild.firstChild.firstChild;
40223 createBody : function(container)
40225 var body = document.createElement("div");
40226 Roo.id(body, "tab-body");
40227 //Roo.fly(body).addClass("x-tabs-body");
40228 Roo.fly(body).addClass("tab-content");
40229 container.appendChild(body);
40232 createItemBody :function(bodyEl, id){
40233 var body = Roo.getDom(id);
40235 body = document.createElement("div");
40238 //Roo.fly(body).addClass("x-tabs-item-body");
40239 Roo.fly(body).addClass("tab-pane");
40240 bodyEl.insertBefore(body, bodyEl.firstChild);
40244 createStripElements : function(stripEl, text, closable, tpl)
40246 var td = document.createElement("li"); // was td..
40247 td.className = 'nav-item';
40249 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40252 stripEl.appendChild(td);
40254 td.className = "x-tabs-closable";
40255 if(!this.closeTpl){
40256 this.closeTpl = new Roo.Template(
40257 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40258 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40259 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40262 var el = this.closeTpl.overwrite(td, {"text": text});
40263 var close = el.getElementsByTagName("div")[0];
40264 var inner = el.getElementsByTagName("em")[0];
40265 return {"el": el, "close": close, "inner": inner};
40268 // not sure what this is..
40269 // if(!this.tabTpl){
40270 //this.tabTpl = new Roo.Template(
40271 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40272 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40274 // this.tabTpl = new Roo.Template(
40275 // '<a href="#">' +
40276 // '<span unselectable="on"' +
40277 // (this.disableTooltips ? '' : ' title="{text}"') +
40278 // ' >{text}</span></a>'
40284 var template = tpl || this.tabTpl || false;
40287 template = new Roo.Template(
40288 Roo.bootstrap.version == 4 ?
40290 '<a class="nav-link" href="#" unselectable="on"' +
40291 (this.disableTooltips ? '' : ' title="{text}"') +
40294 '<a class="nav-link" href="#">' +
40295 '<span unselectable="on"' +
40296 (this.disableTooltips ? '' : ' title="{text}"') +
40297 ' >{text}</span></a>'
40302 switch (typeof(template)) {
40306 template = new Roo.Template(template);
40312 var el = template.overwrite(td, {"text": text});
40314 var inner = el.getElementsByTagName("span")[0];
40316 return {"el": el, "inner": inner};
40324 * @class Roo.TabPanelItem
40325 * @extends Roo.util.Observable
40326 * Represents an individual item (tab plus body) in a TabPanel.
40327 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40328 * @param {String} id The id of this TabPanelItem
40329 * @param {String} text The text for the tab of this TabPanelItem
40330 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40332 Roo.bootstrap.panel.TabItem = function(config){
40334 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40335 * @type Roo.TabPanel
40337 this.tabPanel = config.panel;
40339 * The id for this TabPanelItem
40342 this.id = config.id;
40344 this.disabled = false;
40346 this.text = config.text;
40348 this.loaded = false;
40349 this.closable = config.closable;
40352 * The body element for this TabPanelItem.
40353 * @type Roo.Element
40355 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40356 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40357 this.bodyEl.setStyle("display", "block");
40358 this.bodyEl.setStyle("zoom", "1");
40359 //this.hideAction();
40361 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40363 this.el = Roo.get(els.el);
40364 this.inner = Roo.get(els.inner, true);
40365 this.textEl = Roo.bootstrap.version == 4 ?
40366 this.el : Roo.get(this.el.dom.firstChild, true);
40368 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40369 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40372 // this.el.on("mousedown", this.onTabMouseDown, this);
40373 this.el.on("click", this.onTabClick, this);
40375 if(config.closable){
40376 var c = Roo.get(els.close, true);
40377 c.dom.title = this.closeText;
40378 c.addClassOnOver("close-over");
40379 c.on("click", this.closeClick, this);
40385 * Fires when this tab becomes the active tab.
40386 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40387 * @param {Roo.TabPanelItem} this
40391 * @event beforeclose
40392 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40393 * @param {Roo.TabPanelItem} this
40394 * @param {Object} e Set cancel to true on this object to cancel the close.
40396 "beforeclose": true,
40399 * Fires when this tab is closed.
40400 * @param {Roo.TabPanelItem} this
40404 * @event deactivate
40405 * Fires when this tab is no longer the active tab.
40406 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40407 * @param {Roo.TabPanelItem} this
40409 "deactivate" : true
40411 this.hidden = false;
40413 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40416 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40418 purgeListeners : function(){
40419 Roo.util.Observable.prototype.purgeListeners.call(this);
40420 this.el.removeAllListeners();
40423 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40426 this.status_node.addClass("active");
40429 this.tabPanel.stripWrap.repaint();
40431 this.fireEvent("activate", this.tabPanel, this);
40435 * Returns true if this tab is the active tab.
40436 * @return {Boolean}
40438 isActive : function(){
40439 return this.tabPanel.getActiveTab() == this;
40443 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40446 this.status_node.removeClass("active");
40448 this.fireEvent("deactivate", this.tabPanel, this);
40451 hideAction : function(){
40452 this.bodyEl.hide();
40453 this.bodyEl.setStyle("position", "absolute");
40454 this.bodyEl.setLeft("-20000px");
40455 this.bodyEl.setTop("-20000px");
40458 showAction : function(){
40459 this.bodyEl.setStyle("position", "relative");
40460 this.bodyEl.setTop("");
40461 this.bodyEl.setLeft("");
40462 this.bodyEl.show();
40466 * Set the tooltip for the tab.
40467 * @param {String} tooltip The tab's tooltip
40469 setTooltip : function(text){
40470 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40471 this.textEl.dom.qtip = text;
40472 this.textEl.dom.removeAttribute('title');
40474 this.textEl.dom.title = text;
40478 onTabClick : function(e){
40479 e.preventDefault();
40480 this.tabPanel.activate(this.id);
40483 onTabMouseDown : function(e){
40484 e.preventDefault();
40485 this.tabPanel.activate(this.id);
40488 getWidth : function(){
40489 return this.inner.getWidth();
40492 setWidth : function(width){
40493 var iwidth = width - this.linode.getPadding("lr");
40494 this.inner.setWidth(iwidth);
40495 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40496 this.linode.setWidth(width);
40500 * Show or hide the tab
40501 * @param {Boolean} hidden True to hide or false to show.
40503 setHidden : function(hidden){
40504 this.hidden = hidden;
40505 this.linode.setStyle("display", hidden ? "none" : "");
40509 * Returns true if this tab is "hidden"
40510 * @return {Boolean}
40512 isHidden : function(){
40513 return this.hidden;
40517 * Returns the text for this tab
40520 getText : function(){
40524 autoSize : function(){
40525 //this.el.beginMeasure();
40526 this.textEl.setWidth(1);
40528 * #2804 [new] Tabs in Roojs
40529 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40531 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40532 //this.el.endMeasure();
40536 * Sets the text for the tab (Note: this also sets the tooltip text)
40537 * @param {String} text The tab's text and tooltip
40539 setText : function(text){
40541 this.textEl.update(text);
40542 this.setTooltip(text);
40543 //if(!this.tabPanel.resizeTabs){
40544 // this.autoSize();
40548 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40550 activate : function(){
40551 this.tabPanel.activate(this.id);
40555 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40557 disable : function(){
40558 if(this.tabPanel.active != this){
40559 this.disabled = true;
40560 this.status_node.addClass("disabled");
40565 * Enables this TabPanelItem if it was previously disabled.
40567 enable : function(){
40568 this.disabled = false;
40569 this.status_node.removeClass("disabled");
40573 * Sets the content for this TabPanelItem.
40574 * @param {String} content The content
40575 * @param {Boolean} loadScripts true to look for and load scripts
40577 setContent : function(content, loadScripts){
40578 this.bodyEl.update(content, loadScripts);
40582 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40583 * @return {Roo.UpdateManager} The UpdateManager
40585 getUpdateManager : function(){
40586 return this.bodyEl.getUpdateManager();
40590 * Set a URL to be used to load the content for this TabPanelItem.
40591 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40592 * @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)
40593 * @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)
40594 * @return {Roo.UpdateManager} The UpdateManager
40596 setUrl : function(url, params, loadOnce){
40597 if(this.refreshDelegate){
40598 this.un('activate', this.refreshDelegate);
40600 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40601 this.on("activate", this.refreshDelegate);
40602 return this.bodyEl.getUpdateManager();
40606 _handleRefresh : function(url, params, loadOnce){
40607 if(!loadOnce || !this.loaded){
40608 var updater = this.bodyEl.getUpdateManager();
40609 updater.update(url, params, this._setLoaded.createDelegate(this));
40614 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40615 * Will fail silently if the setUrl method has not been called.
40616 * This does not activate the panel, just updates its content.
40618 refresh : function(){
40619 if(this.refreshDelegate){
40620 this.loaded = false;
40621 this.refreshDelegate();
40626 _setLoaded : function(){
40627 this.loaded = true;
40631 closeClick : function(e){
40634 this.fireEvent("beforeclose", this, o);
40635 if(o.cancel !== true){
40636 this.tabPanel.removeTab(this.id);
40640 * The text displayed in the tooltip for the close icon.
40643 closeText : "Close this tab"
40646 * This script refer to:
40647 * Title: International Telephone Input
40648 * Author: Jack O'Connor
40649 * Code version: v12.1.12
40650 * Availability: https://github.com/jackocnr/intl-tel-input.git
40653 Roo.bootstrap.PhoneInputData = function() {
40656 "Afghanistan (افغانستان)",
40661 "Albania (Shqipëri)",
40666 "Algeria (الجزائر)",
40691 "Antigua and Barbuda",
40701 "Armenia (Հայաստան)",
40717 "Austria (Österreich)",
40722 "Azerbaijan (Azərbaycan)",
40732 "Bahrain (البحرين)",
40737 "Bangladesh (বাংলাদেশ)",
40747 "Belarus (Беларусь)",
40752 "Belgium (België)",
40782 "Bosnia and Herzegovina (Босна и Херцеговина)",
40797 "British Indian Ocean Territory",
40802 "British Virgin Islands",
40812 "Bulgaria (България)",
40822 "Burundi (Uburundi)",
40827 "Cambodia (កម្ពុជា)",
40832 "Cameroon (Cameroun)",
40841 ["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"]
40844 "Cape Verde (Kabu Verdi)",
40849 "Caribbean Netherlands",
40860 "Central African Republic (République centrafricaine)",
40880 "Christmas Island",
40886 "Cocos (Keeling) Islands",
40897 "Comoros (جزر القمر)",
40902 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40907 "Congo (Republic) (Congo-Brazzaville)",
40927 "Croatia (Hrvatska)",
40948 "Czech Republic (Česká republika)",
40953 "Denmark (Danmark)",
40968 "Dominican Republic (República Dominicana)",
40972 ["809", "829", "849"]
40990 "Equatorial Guinea (Guinea Ecuatorial)",
41010 "Falkland Islands (Islas Malvinas)",
41015 "Faroe Islands (Føroyar)",
41036 "French Guiana (Guyane française)",
41041 "French Polynesia (Polynésie française)",
41056 "Georgia (საქართველო)",
41061 "Germany (Deutschland)",
41081 "Greenland (Kalaallit Nunaat)",
41118 "Guinea-Bissau (Guiné Bissau)",
41143 "Hungary (Magyarország)",
41148 "Iceland (Ísland)",
41168 "Iraq (العراق)",
41184 "Israel (ישראל)",
41211 "Jordan (الأردن)",
41216 "Kazakhstan (Казахстан)",
41237 "Kuwait (الكويت)",
41242 "Kyrgyzstan (Кыргызстан)",
41252 "Latvia (Latvija)",
41257 "Lebanon (لبنان)",
41272 "Libya (ليبيا)",
41282 "Lithuania (Lietuva)",
41297 "Macedonia (FYROM) (Македонија)",
41302 "Madagascar (Madagasikara)",
41332 "Marshall Islands",
41342 "Mauritania (موريتانيا)",
41347 "Mauritius (Moris)",
41368 "Moldova (Republica Moldova)",
41378 "Mongolia (Монгол)",
41383 "Montenegro (Crna Gora)",
41393 "Morocco (المغرب)",
41399 "Mozambique (Moçambique)",
41404 "Myanmar (Burma) (မြန်မာ)",
41409 "Namibia (Namibië)",
41424 "Netherlands (Nederland)",
41429 "New Caledonia (Nouvelle-Calédonie)",
41464 "North Korea (조선 민주주의 인민 공화국)",
41469 "Northern Mariana Islands",
41485 "Pakistan (پاکستان)",
41495 "Palestine (فلسطين)",
41505 "Papua New Guinea",
41547 "Réunion (La Réunion)",
41553 "Romania (România)",
41569 "Saint Barthélemy",
41580 "Saint Kitts and Nevis",
41590 "Saint Martin (Saint-Martin (partie française))",
41596 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41601 "Saint Vincent and the Grenadines",
41616 "São Tomé and Príncipe (São Tomé e Príncipe)",
41621 "Saudi Arabia (المملكة العربية السعودية)",
41626 "Senegal (Sénégal)",
41656 "Slovakia (Slovensko)",
41661 "Slovenia (Slovenija)",
41671 "Somalia (Soomaaliya)",
41681 "South Korea (대한민국)",
41686 "South Sudan (جنوب السودان)",
41696 "Sri Lanka (ශ්රී ලංකාව)",
41701 "Sudan (السودان)",
41711 "Svalbard and Jan Mayen",
41722 "Sweden (Sverige)",
41727 "Switzerland (Schweiz)",
41732 "Syria (سوريا)",
41777 "Trinidad and Tobago",
41782 "Tunisia (تونس)",
41787 "Turkey (Türkiye)",
41797 "Turks and Caicos Islands",
41807 "U.S. Virgin Islands",
41817 "Ukraine (Україна)",
41822 "United Arab Emirates (الإمارات العربية المتحدة)",
41844 "Uzbekistan (Oʻzbekiston)",
41854 "Vatican City (Città del Vaticano)",
41865 "Vietnam (Việt Nam)",
41870 "Wallis and Futuna (Wallis-et-Futuna)",
41875 "Western Sahara (الصحراء الغربية)",
41881 "Yemen (اليمن)",
41905 * This script refer to:
41906 * Title: International Telephone Input
41907 * Author: Jack O'Connor
41908 * Code version: v12.1.12
41909 * Availability: https://github.com/jackocnr/intl-tel-input.git
41913 * @class Roo.bootstrap.PhoneInput
41914 * @extends Roo.bootstrap.TriggerField
41915 * An input with International dial-code selection
41917 * @cfg {String} defaultDialCode default '+852'
41918 * @cfg {Array} preferedCountries default []
41921 * Create a new PhoneInput.
41922 * @param {Object} config Configuration options
41925 Roo.bootstrap.PhoneInput = function(config) {
41926 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41929 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41931 listWidth: undefined,
41933 selectedClass: 'active',
41935 invalidClass : "has-warning",
41937 validClass: 'has-success',
41939 allowed: '0123456789',
41944 * @cfg {String} defaultDialCode The default dial code when initializing the input
41946 defaultDialCode: '+852',
41949 * @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
41951 preferedCountries: false,
41953 getAutoCreate : function()
41955 var data = Roo.bootstrap.PhoneInputData();
41956 var align = this.labelAlign || this.parentLabelAlign();
41959 this.allCountries = [];
41960 this.dialCodeMapping = [];
41962 for (var i = 0; i < data.length; i++) {
41964 this.allCountries[i] = {
41968 priority: c[3] || 0,
41969 areaCodes: c[4] || null
41971 this.dialCodeMapping[c[2]] = {
41974 priority: c[3] || 0,
41975 areaCodes: c[4] || null
41987 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41988 maxlength: this.max_length,
41989 cls : 'form-control tel-input',
41990 autocomplete: 'new-password'
41993 var hiddenInput = {
41996 cls: 'hidden-tel-input'
42000 hiddenInput.name = this.name;
42003 if (this.disabled) {
42004 input.disabled = true;
42007 var flag_container = {
42024 cls: this.hasFeedback ? 'has-feedback' : '',
42030 cls: 'dial-code-holder',
42037 cls: 'roo-select2-container input-group',
42044 if (this.fieldLabel.length) {
42047 tooltip: 'This field is required'
42053 cls: 'control-label',
42059 html: this.fieldLabel
42062 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42068 if(this.indicatorpos == 'right') {
42069 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42076 if(align == 'left') {
42084 if(this.labelWidth > 12){
42085 label.style = "width: " + this.labelWidth + 'px';
42087 if(this.labelWidth < 13 && this.labelmd == 0){
42088 this.labelmd = this.labelWidth;
42090 if(this.labellg > 0){
42091 label.cls += ' col-lg-' + this.labellg;
42092 input.cls += ' col-lg-' + (12 - this.labellg);
42094 if(this.labelmd > 0){
42095 label.cls += ' col-md-' + this.labelmd;
42096 container.cls += ' col-md-' + (12 - this.labelmd);
42098 if(this.labelsm > 0){
42099 label.cls += ' col-sm-' + this.labelsm;
42100 container.cls += ' col-sm-' + (12 - this.labelsm);
42102 if(this.labelxs > 0){
42103 label.cls += ' col-xs-' + this.labelxs;
42104 container.cls += ' col-xs-' + (12 - this.labelxs);
42114 var settings = this;
42116 ['xs','sm','md','lg'].map(function(size){
42117 if (settings[size]) {
42118 cfg.cls += ' col-' + size + '-' + settings[size];
42122 this.store = new Roo.data.Store({
42123 proxy : new Roo.data.MemoryProxy({}),
42124 reader : new Roo.data.JsonReader({
42135 'name' : 'dialCode',
42139 'name' : 'priority',
42143 'name' : 'areaCodes',
42150 if(!this.preferedCountries) {
42151 this.preferedCountries = [
42158 var p = this.preferedCountries.reverse();
42161 for (var i = 0; i < p.length; i++) {
42162 for (var j = 0; j < this.allCountries.length; j++) {
42163 if(this.allCountries[j].iso2 == p[i]) {
42164 var t = this.allCountries[j];
42165 this.allCountries.splice(j,1);
42166 this.allCountries.unshift(t);
42172 this.store.proxy.data = {
42174 data: this.allCountries
42180 initEvents : function()
42183 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42185 this.indicator = this.indicatorEl();
42186 this.flag = this.flagEl();
42187 this.dialCodeHolder = this.dialCodeHolderEl();
42189 this.trigger = this.el.select('div.flag-box',true).first();
42190 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42195 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42196 _this.list.setWidth(lw);
42199 this.list.on('mouseover', this.onViewOver, this);
42200 this.list.on('mousemove', this.onViewMove, this);
42201 this.inputEl().on("keyup", this.onKeyUp, this);
42202 this.inputEl().on("keypress", this.onKeyPress, this);
42204 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42206 this.view = new Roo.View(this.list, this.tpl, {
42207 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42210 this.view.on('click', this.onViewClick, this);
42211 this.setValue(this.defaultDialCode);
42214 onTriggerClick : function(e)
42216 Roo.log('trigger click');
42221 if(this.isExpanded()){
42223 this.hasFocus = false;
42225 this.store.load({});
42226 this.hasFocus = true;
42231 isExpanded : function()
42233 return this.list.isVisible();
42236 collapse : function()
42238 if(!this.isExpanded()){
42242 Roo.get(document).un('mousedown', this.collapseIf, this);
42243 Roo.get(document).un('mousewheel', this.collapseIf, this);
42244 this.fireEvent('collapse', this);
42248 expand : function()
42252 if(this.isExpanded() || !this.hasFocus){
42256 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42257 this.list.setWidth(lw);
42260 this.restrictHeight();
42262 Roo.get(document).on('mousedown', this.collapseIf, this);
42263 Roo.get(document).on('mousewheel', this.collapseIf, this);
42265 this.fireEvent('expand', this);
42268 restrictHeight : function()
42270 this.list.alignTo(this.inputEl(), this.listAlign);
42271 this.list.alignTo(this.inputEl(), this.listAlign);
42274 onViewOver : function(e, t)
42276 if(this.inKeyMode){
42279 var item = this.view.findItemFromChild(t);
42282 var index = this.view.indexOf(item);
42283 this.select(index, false);
42288 onViewClick : function(view, doFocus, el, e)
42290 var index = this.view.getSelectedIndexes()[0];
42292 var r = this.store.getAt(index);
42295 this.onSelect(r, index);
42297 if(doFocus !== false && !this.blockFocus){
42298 this.inputEl().focus();
42302 onViewMove : function(e, t)
42304 this.inKeyMode = false;
42307 select : function(index, scrollIntoView)
42309 this.selectedIndex = index;
42310 this.view.select(index);
42311 if(scrollIntoView !== false){
42312 var el = this.view.getNode(index);
42314 this.list.scrollChildIntoView(el, false);
42319 createList : function()
42321 this.list = Roo.get(document.body).createChild({
42323 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42324 style: 'display:none'
42327 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42330 collapseIf : function(e)
42332 var in_combo = e.within(this.el);
42333 var in_list = e.within(this.list);
42334 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42336 if (in_combo || in_list || is_list) {
42342 onSelect : function(record, index)
42344 if(this.fireEvent('beforeselect', this, record, index) !== false){
42346 this.setFlagClass(record.data.iso2);
42347 this.setDialCode(record.data.dialCode);
42348 this.hasFocus = false;
42350 this.fireEvent('select', this, record, index);
42354 flagEl : function()
42356 var flag = this.el.select('div.flag',true).first();
42363 dialCodeHolderEl : function()
42365 var d = this.el.select('input.dial-code-holder',true).first();
42372 setDialCode : function(v)
42374 this.dialCodeHolder.dom.value = '+'+v;
42377 setFlagClass : function(n)
42379 this.flag.dom.className = 'flag '+n;
42382 getValue : function()
42384 var v = this.inputEl().getValue();
42385 if(this.dialCodeHolder) {
42386 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42391 setValue : function(v)
42393 var d = this.getDialCode(v);
42395 //invalid dial code
42396 if(v.length == 0 || !d || d.length == 0) {
42398 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42399 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42405 this.setFlagClass(this.dialCodeMapping[d].iso2);
42406 this.setDialCode(d);
42407 this.inputEl().dom.value = v.replace('+'+d,'');
42408 this.hiddenEl().dom.value = this.getValue();
42413 getDialCode : function(v)
42417 if (v.length == 0) {
42418 return this.dialCodeHolder.dom.value;
42422 if (v.charAt(0) != "+") {
42425 var numericChars = "";
42426 for (var i = 1; i < v.length; i++) {
42427 var c = v.charAt(i);
42430 if (this.dialCodeMapping[numericChars]) {
42431 dialCode = v.substr(1, i);
42433 if (numericChars.length == 4) {
42443 this.setValue(this.defaultDialCode);
42447 hiddenEl : function()
42449 return this.el.select('input.hidden-tel-input',true).first();
42452 // after setting val
42453 onKeyUp : function(e){
42454 this.setValue(this.getValue());
42457 onKeyPress : function(e){
42458 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42465 * @class Roo.bootstrap.MoneyField
42466 * @extends Roo.bootstrap.ComboBox
42467 * Bootstrap MoneyField class
42470 * Create a new MoneyField.
42471 * @param {Object} config Configuration options
42474 Roo.bootstrap.MoneyField = function(config) {
42476 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42480 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42483 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42485 allowDecimals : true,
42487 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42489 decimalSeparator : ".",
42491 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42493 decimalPrecision : 0,
42495 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42497 allowNegative : true,
42499 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42503 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42505 minValue : Number.NEGATIVE_INFINITY,
42507 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42509 maxValue : Number.MAX_VALUE,
42511 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42513 minText : "The minimum value for this field is {0}",
42515 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42517 maxText : "The maximum value for this field is {0}",
42519 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42520 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42522 nanText : "{0} is not a valid number",
42524 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42528 * @cfg {String} defaults currency of the MoneyField
42529 * value should be in lkey
42531 defaultCurrency : false,
42533 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42535 thousandsDelimiter : false,
42537 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42548 getAutoCreate : function()
42550 var align = this.labelAlign || this.parentLabelAlign();
42562 cls : 'form-control roo-money-amount-input',
42563 autocomplete: 'new-password'
42566 var hiddenInput = {
42570 cls: 'hidden-number-input'
42573 if(this.max_length) {
42574 input.maxlength = this.max_length;
42578 hiddenInput.name = this.name;
42581 if (this.disabled) {
42582 input.disabled = true;
42585 var clg = 12 - this.inputlg;
42586 var cmd = 12 - this.inputmd;
42587 var csm = 12 - this.inputsm;
42588 var cxs = 12 - this.inputxs;
42592 cls : 'row roo-money-field',
42596 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42600 cls: 'roo-select2-container input-group',
42604 cls : 'form-control roo-money-currency-input',
42605 autocomplete: 'new-password',
42607 name : this.currencyName
42611 cls : 'input-group-addon',
42625 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42629 cls: this.hasFeedback ? 'has-feedback' : '',
42640 if (this.fieldLabel.length) {
42643 tooltip: 'This field is required'
42649 cls: 'control-label',
42655 html: this.fieldLabel
42658 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42664 if(this.indicatorpos == 'right') {
42665 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42672 if(align == 'left') {
42680 if(this.labelWidth > 12){
42681 label.style = "width: " + this.labelWidth + 'px';
42683 if(this.labelWidth < 13 && this.labelmd == 0){
42684 this.labelmd = this.labelWidth;
42686 if(this.labellg > 0){
42687 label.cls += ' col-lg-' + this.labellg;
42688 input.cls += ' col-lg-' + (12 - this.labellg);
42690 if(this.labelmd > 0){
42691 label.cls += ' col-md-' + this.labelmd;
42692 container.cls += ' col-md-' + (12 - this.labelmd);
42694 if(this.labelsm > 0){
42695 label.cls += ' col-sm-' + this.labelsm;
42696 container.cls += ' col-sm-' + (12 - this.labelsm);
42698 if(this.labelxs > 0){
42699 label.cls += ' col-xs-' + this.labelxs;
42700 container.cls += ' col-xs-' + (12 - this.labelxs);
42711 var settings = this;
42713 ['xs','sm','md','lg'].map(function(size){
42714 if (settings[size]) {
42715 cfg.cls += ' col-' + size + '-' + settings[size];
42722 initEvents : function()
42724 this.indicator = this.indicatorEl();
42726 this.initCurrencyEvent();
42728 this.initNumberEvent();
42731 initCurrencyEvent : function()
42734 throw "can not find store for combo";
42737 this.store = Roo.factory(this.store, Roo.data);
42738 this.store.parent = this;
42742 this.triggerEl = this.el.select('.input-group-addon', true).first();
42744 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42749 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42750 _this.list.setWidth(lw);
42753 this.list.on('mouseover', this.onViewOver, this);
42754 this.list.on('mousemove', this.onViewMove, this);
42755 this.list.on('scroll', this.onViewScroll, this);
42758 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42761 this.view = new Roo.View(this.list, this.tpl, {
42762 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42765 this.view.on('click', this.onViewClick, this);
42767 this.store.on('beforeload', this.onBeforeLoad, this);
42768 this.store.on('load', this.onLoad, this);
42769 this.store.on('loadexception', this.onLoadException, this);
42771 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42772 "up" : function(e){
42773 this.inKeyMode = true;
42777 "down" : function(e){
42778 if(!this.isExpanded()){
42779 this.onTriggerClick();
42781 this.inKeyMode = true;
42786 "enter" : function(e){
42789 if(this.fireEvent("specialkey", this, e)){
42790 this.onViewClick(false);
42796 "esc" : function(e){
42800 "tab" : function(e){
42803 if(this.fireEvent("specialkey", this, e)){
42804 this.onViewClick(false);
42812 doRelay : function(foo, bar, hname){
42813 if(hname == 'down' || this.scope.isExpanded()){
42814 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42822 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42826 initNumberEvent : function(e)
42828 this.inputEl().on("keydown" , this.fireKey, this);
42829 this.inputEl().on("focus", this.onFocus, this);
42830 this.inputEl().on("blur", this.onBlur, this);
42832 this.inputEl().relayEvent('keyup', this);
42834 if(this.indicator){
42835 this.indicator.addClass('invisible');
42838 this.originalValue = this.getValue();
42840 if(this.validationEvent == 'keyup'){
42841 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42842 this.inputEl().on('keyup', this.filterValidation, this);
42844 else if(this.validationEvent !== false){
42845 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42848 if(this.selectOnFocus){
42849 this.on("focus", this.preFocus, this);
42852 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42853 this.inputEl().on("keypress", this.filterKeys, this);
42855 this.inputEl().relayEvent('keypress', this);
42858 var allowed = "0123456789";
42860 if(this.allowDecimals){
42861 allowed += this.decimalSeparator;
42864 if(this.allowNegative){
42868 if(this.thousandsDelimiter) {
42872 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42874 var keyPress = function(e){
42876 var k = e.getKey();
42878 var c = e.getCharCode();
42881 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42882 allowed.indexOf(String.fromCharCode(c)) === -1
42888 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42892 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42897 this.inputEl().on("keypress", keyPress, this);
42901 onTriggerClick : function(e)
42908 this.loadNext = false;
42910 if(this.isExpanded()){
42915 this.hasFocus = true;
42917 if(this.triggerAction == 'all') {
42918 this.doQuery(this.allQuery, true);
42922 this.doQuery(this.getRawValue());
42925 getCurrency : function()
42927 var v = this.currencyEl().getValue();
42932 restrictHeight : function()
42934 this.list.alignTo(this.currencyEl(), this.listAlign);
42935 this.list.alignTo(this.currencyEl(), this.listAlign);
42938 onViewClick : function(view, doFocus, el, e)
42940 var index = this.view.getSelectedIndexes()[0];
42942 var r = this.store.getAt(index);
42945 this.onSelect(r, index);
42949 onSelect : function(record, index){
42951 if(this.fireEvent('beforeselect', this, record, index) !== false){
42953 this.setFromCurrencyData(index > -1 ? record.data : false);
42957 this.fireEvent('select', this, record, index);
42961 setFromCurrencyData : function(o)
42965 this.lastCurrency = o;
42967 if (this.currencyField) {
42968 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42970 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42973 this.lastSelectionText = currency;
42975 //setting default currency
42976 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42977 this.setCurrency(this.defaultCurrency);
42981 this.setCurrency(currency);
42984 setFromData : function(o)
42988 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42990 this.setFromCurrencyData(c);
42995 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42997 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43000 this.setValue(value);
43004 setCurrency : function(v)
43006 this.currencyValue = v;
43009 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43014 setValue : function(v)
43016 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43022 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43024 this.inputEl().dom.value = (v == '') ? '' :
43025 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43027 if(!this.allowZero && v === '0') {
43028 this.hiddenEl().dom.value = '';
43029 this.inputEl().dom.value = '';
43036 getRawValue : function()
43038 var v = this.inputEl().getValue();
43043 getValue : function()
43045 return this.fixPrecision(this.parseValue(this.getRawValue()));
43048 parseValue : function(value)
43050 if(this.thousandsDelimiter) {
43052 r = new RegExp(",", "g");
43053 value = value.replace(r, "");
43056 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43057 return isNaN(value) ? '' : value;
43061 fixPrecision : function(value)
43063 if(this.thousandsDelimiter) {
43065 r = new RegExp(",", "g");
43066 value = value.replace(r, "");
43069 var nan = isNaN(value);
43071 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43072 return nan ? '' : value;
43074 return parseFloat(value).toFixed(this.decimalPrecision);
43077 decimalPrecisionFcn : function(v)
43079 return Math.floor(v);
43082 validateValue : function(value)
43084 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43088 var num = this.parseValue(value);
43091 this.markInvalid(String.format(this.nanText, value));
43095 if(num < this.minValue){
43096 this.markInvalid(String.format(this.minText, this.minValue));
43100 if(num > this.maxValue){
43101 this.markInvalid(String.format(this.maxText, this.maxValue));
43108 validate : function()
43110 if(this.disabled || this.allowBlank){
43115 var currency = this.getCurrency();
43117 if(this.validateValue(this.getRawValue()) && currency.length){
43122 this.markInvalid();
43126 getName: function()
43131 beforeBlur : function()
43137 var v = this.parseValue(this.getRawValue());
43144 onBlur : function()
43148 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43149 //this.el.removeClass(this.focusClass);
43152 this.hasFocus = false;
43154 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43158 var v = this.getValue();
43160 if(String(v) !== String(this.startValue)){
43161 this.fireEvent('change', this, v, this.startValue);
43164 this.fireEvent("blur", this);
43167 inputEl : function()
43169 return this.el.select('.roo-money-amount-input', true).first();
43172 currencyEl : function()
43174 return this.el.select('.roo-money-currency-input', true).first();
43177 hiddenEl : function()
43179 return this.el.select('input.hidden-number-input',true).first();
43183 * @class Roo.bootstrap.BezierSignature
43184 * @extends Roo.bootstrap.Component
43185 * Bootstrap BezierSignature class
43186 * This script refer to:
43187 * Title: Signature Pad
43189 * Availability: https://github.com/szimek/signature_pad
43192 * Create a new BezierSignature
43193 * @param {Object} config The config object
43196 Roo.bootstrap.BezierSignature = function(config){
43197 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43203 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43210 mouse_btn_down: true,
43213 * @cfg {int} canvas height
43215 canvas_height: '200px',
43218 * @cfg {float|function} Radius of a single dot.
43223 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43228 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43233 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43238 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43243 * @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.
43245 bg_color: 'rgba(0, 0, 0, 0)',
43248 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43250 dot_color: 'black',
43253 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43255 velocity_filter_weight: 0.7,
43258 * @cfg {function} Callback when stroke begin.
43263 * @cfg {function} Callback when stroke end.
43267 getAutoCreate : function()
43269 var cls = 'roo-signature column';
43272 cls += ' ' + this.cls;
43282 for(var i = 0; i < col_sizes.length; i++) {
43283 if(this[col_sizes[i]]) {
43284 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43294 cls: 'roo-signature-body',
43298 cls: 'roo-signature-body-canvas',
43299 height: this.canvas_height,
43300 width: this.canvas_width
43307 style: 'display: none'
43315 initEvents: function()
43317 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43319 var canvas = this.canvasEl();
43321 // mouse && touch event swapping...
43322 canvas.dom.style.touchAction = 'none';
43323 canvas.dom.style.msTouchAction = 'none';
43325 this.mouse_btn_down = false;
43326 canvas.on('mousedown', this._handleMouseDown, this);
43327 canvas.on('mousemove', this._handleMouseMove, this);
43328 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43330 if (window.PointerEvent) {
43331 canvas.on('pointerdown', this._handleMouseDown, this);
43332 canvas.on('pointermove', this._handleMouseMove, this);
43333 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43336 if ('ontouchstart' in window) {
43337 canvas.on('touchstart', this._handleTouchStart, this);
43338 canvas.on('touchmove', this._handleTouchMove, this);
43339 canvas.on('touchend', this._handleTouchEnd, this);
43342 Roo.EventManager.onWindowResize(this.resize, this, true);
43344 // file input event
43345 this.fileEl().on('change', this.uploadImage, this);
43352 resize: function(){
43354 var canvas = this.canvasEl().dom;
43355 var ctx = this.canvasElCtx();
43356 var img_data = false;
43358 if(canvas.width > 0) {
43359 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43361 // setting canvas width will clean img data
43364 var style = window.getComputedStyle ?
43365 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43367 var padding_left = parseInt(style.paddingLeft) || 0;
43368 var padding_right = parseInt(style.paddingRight) || 0;
43370 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43373 ctx.putImageData(img_data, 0, 0);
43377 _handleMouseDown: function(e)
43379 if (e.browserEvent.which === 1) {
43380 this.mouse_btn_down = true;
43381 this.strokeBegin(e);
43385 _handleMouseMove: function (e)
43387 if (this.mouse_btn_down) {
43388 this.strokeMoveUpdate(e);
43392 _handleMouseUp: function (e)
43394 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43395 this.mouse_btn_down = false;
43400 _handleTouchStart: function (e) {
43402 e.preventDefault();
43403 if (e.browserEvent.targetTouches.length === 1) {
43404 // var touch = e.browserEvent.changedTouches[0];
43405 // this.strokeBegin(touch);
43407 this.strokeBegin(e); // assume e catching the correct xy...
43411 _handleTouchMove: function (e) {
43412 e.preventDefault();
43413 // var touch = event.targetTouches[0];
43414 // _this._strokeMoveUpdate(touch);
43415 this.strokeMoveUpdate(e);
43418 _handleTouchEnd: function (e) {
43419 var wasCanvasTouched = e.target === this.canvasEl().dom;
43420 if (wasCanvasTouched) {
43421 e.preventDefault();
43422 // var touch = event.changedTouches[0];
43423 // _this._strokeEnd(touch);
43428 reset: function () {
43429 this._lastPoints = [];
43430 this._lastVelocity = 0;
43431 this._lastWidth = (this.min_width + this.max_width) / 2;
43432 this.canvasElCtx().fillStyle = this.dot_color;
43435 strokeMoveUpdate: function(e)
43437 this.strokeUpdate(e);
43439 if (this.throttle) {
43440 this.throttleStroke(this.strokeUpdate, this.throttle);
43443 this.strokeUpdate(e);
43447 strokeBegin: function(e)
43449 var newPointGroup = {
43450 color: this.dot_color,
43454 if (typeof this.onBegin === 'function') {
43458 this.curve_data.push(newPointGroup);
43460 this.strokeUpdate(e);
43463 strokeUpdate: function(e)
43465 var rect = this.canvasEl().dom.getBoundingClientRect();
43466 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43467 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43468 var lastPoints = lastPointGroup.points;
43469 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43470 var isLastPointTooClose = lastPoint
43471 ? point.distanceTo(lastPoint) <= this.min_distance
43473 var color = lastPointGroup.color;
43474 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43475 var curve = this.addPoint(point);
43477 this.drawDot({color: color, point: point});
43480 this.drawCurve({color: color, curve: curve});
43490 strokeEnd: function(e)
43492 this.strokeUpdate(e);
43493 if (typeof this.onEnd === 'function') {
43498 addPoint: function (point) {
43499 var _lastPoints = this._lastPoints;
43500 _lastPoints.push(point);
43501 if (_lastPoints.length > 2) {
43502 if (_lastPoints.length === 3) {
43503 _lastPoints.unshift(_lastPoints[0]);
43505 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43506 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43507 _lastPoints.shift();
43513 calculateCurveWidths: function (startPoint, endPoint) {
43514 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43515 (1 - this.velocity_filter_weight) * this._lastVelocity;
43517 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43520 start: this._lastWidth
43523 this._lastVelocity = velocity;
43524 this._lastWidth = newWidth;
43528 drawDot: function (_a) {
43529 var color = _a.color, point = _a.point;
43530 var ctx = this.canvasElCtx();
43531 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43533 this.drawCurveSegment(point.x, point.y, width);
43535 ctx.fillStyle = color;
43539 drawCurve: function (_a) {
43540 var color = _a.color, curve = _a.curve;
43541 var ctx = this.canvasElCtx();
43542 var widthDelta = curve.endWidth - curve.startWidth;
43543 var drawSteps = Math.floor(curve.length()) * 2;
43545 ctx.fillStyle = color;
43546 for (var i = 0; i < drawSteps; i += 1) {
43547 var t = i / drawSteps;
43553 var x = uuu * curve.startPoint.x;
43554 x += 3 * uu * t * curve.control1.x;
43555 x += 3 * u * tt * curve.control2.x;
43556 x += ttt * curve.endPoint.x;
43557 var y = uuu * curve.startPoint.y;
43558 y += 3 * uu * t * curve.control1.y;
43559 y += 3 * u * tt * curve.control2.y;
43560 y += ttt * curve.endPoint.y;
43561 var width = curve.startWidth + ttt * widthDelta;
43562 this.drawCurveSegment(x, y, width);
43568 drawCurveSegment: function (x, y, width) {
43569 var ctx = this.canvasElCtx();
43571 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43572 this.is_empty = false;
43577 var ctx = this.canvasElCtx();
43578 var canvas = this.canvasEl().dom;
43579 ctx.fillStyle = this.bg_color;
43580 ctx.clearRect(0, 0, canvas.width, canvas.height);
43581 ctx.fillRect(0, 0, canvas.width, canvas.height);
43582 this.curve_data = [];
43584 this.is_empty = true;
43589 return this.el.select('input',true).first();
43592 canvasEl: function()
43594 return this.el.select('canvas',true).first();
43597 canvasElCtx: function()
43599 return this.el.select('canvas',true).first().dom.getContext('2d');
43602 getImage: function(type)
43604 if(this.is_empty) {
43609 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43612 drawFromImage: function(img_src)
43614 var img = new Image();
43616 img.onload = function(){
43617 this.canvasElCtx().drawImage(img, 0, 0);
43622 this.is_empty = false;
43625 selectImage: function()
43627 this.fileEl().dom.click();
43630 uploadImage: function(e)
43632 var reader = new FileReader();
43634 reader.onload = function(e){
43635 var img = new Image();
43636 img.onload = function(){
43638 this.canvasElCtx().drawImage(img, 0, 0);
43640 img.src = e.target.result;
43643 reader.readAsDataURL(e.target.files[0]);
43646 // Bezier Point Constructor
43647 Point: (function () {
43648 function Point(x, y, time) {
43651 this.time = time || Date.now();
43653 Point.prototype.distanceTo = function (start) {
43654 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43656 Point.prototype.equals = function (other) {
43657 return this.x === other.x && this.y === other.y && this.time === other.time;
43659 Point.prototype.velocityFrom = function (start) {
43660 return this.time !== start.time
43661 ? this.distanceTo(start) / (this.time - start.time)
43668 // Bezier Constructor
43669 Bezier: (function () {
43670 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43671 this.startPoint = startPoint;
43672 this.control2 = control2;
43673 this.control1 = control1;
43674 this.endPoint = endPoint;
43675 this.startWidth = startWidth;
43676 this.endWidth = endWidth;
43678 Bezier.fromPoints = function (points, widths, scope) {
43679 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43680 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43681 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43683 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43684 var dx1 = s1.x - s2.x;
43685 var dy1 = s1.y - s2.y;
43686 var dx2 = s2.x - s3.x;
43687 var dy2 = s2.y - s3.y;
43688 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43689 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43690 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43691 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43692 var dxm = m1.x - m2.x;
43693 var dym = m1.y - m2.y;
43694 var k = l2 / (l1 + l2);
43695 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43696 var tx = s2.x - cm.x;
43697 var ty = s2.y - cm.y;
43699 c1: new scope.Point(m1.x + tx, m1.y + ty),
43700 c2: new scope.Point(m2.x + tx, m2.y + ty)
43703 Bezier.prototype.length = function () {
43708 for (var i = 0; i <= steps; i += 1) {
43710 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43711 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43713 var xdiff = cx - px;
43714 var ydiff = cy - py;
43715 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43722 Bezier.prototype.point = function (t, start, c1, c2, end) {
43723 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43724 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43725 + (3.0 * c2 * (1.0 - t) * t * t)
43726 + (end * t * t * t);
43731 throttleStroke: function(fn, wait) {
43732 if (wait === void 0) { wait = 250; }
43734 var timeout = null;
43738 var later = function () {
43739 previous = Date.now();
43741 result = fn.apply(storedContext, storedArgs);
43743 storedContext = null;
43747 return function wrapper() {
43749 for (var _i = 0; _i < arguments.length; _i++) {
43750 args[_i] = arguments[_i];
43752 var now = Date.now();
43753 var remaining = wait - (now - previous);
43754 storedContext = this;
43756 if (remaining <= 0 || remaining > wait) {
43758 clearTimeout(timeout);
43762 result = fn.apply(storedContext, storedArgs);
43764 storedContext = null;
43768 else if (!timeout) {
43769 timeout = window.setTimeout(later, remaining);