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 var hdr_ctr = false;
2095 if (this.header.length) {
2097 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2098 cls : 'card-header',
2106 cls : 'card-header d-none',
2112 if (this.collapsable) {
2115 cls : 'd-block user-select-none',
2119 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2124 hdr.cn.push(hdr_ctr);
2129 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2134 if (this.header_image.length) {
2137 cls : 'card-img-top',
2138 src: this.header_image // escape?
2143 cls : 'card-img-top d-none'
2149 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2153 if (this.collapsable || this.rotateable) {
2156 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2163 if (this.title.length) {
2167 src: this.title // escape?
2171 if (this.subtitle.length) {
2175 src: this.subtitle // escape?
2181 cls : 'roo-card-body-ctr'
2184 if (this.html.length) {
2190 // fixme ? handle objects?
2192 if (this.footer.length) {
2195 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2200 cfg.cn.push({cls : 'card-footer d-none'});
2209 getCardHeader : function()
2211 var ret = this.el.select('.card-header',true).first();
2212 if (ret.hasClass('d-none')) {
2213 ret.removeClass('d-none');
2218 getCardFooter : function()
2220 var ret = this.el.select('.card-footer',true).first();
2221 if (ret.hasClass('d-none')) {
2222 ret.removeClass('d-none');
2227 getCardImageTop : function()
2229 var ret = this.el.select('.card-img-top',true).first();
2230 if (ret.hasClass('d-none')) {
2231 ret.removeClass('d-none');
2237 getChildContainer : function()
2243 return this.el.select('.roo-card-body-ctr',true).first();
2246 initEvents: function()
2249 this.bodyEl = this.getChildContainer();
2251 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2252 containerScroll: true,
2253 ddGroup: this.drag_group || 'default_card_drag_group'
2255 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2257 if (this.dropable) {
2258 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2259 containerScroll: true,
2260 ddGroup: this.drop_group || 'default_card_drag_group'
2262 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2263 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2264 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2265 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2266 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2269 if (this.collapsable) {
2270 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2272 if (this.rotateable) {
2273 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2275 this.collapsableEl = this.el.select('.roo-collapsable').first();
2277 this.footerEl = this.el.select('.card-footer').first();
2278 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2279 this.headerEl = this.el.select('.roo-card-header-ctr').first();
2282 this.el.addClass('roo-card-rotated');
2283 this.fireEvent('rotate', this, true);
2287 getDragData : function(e)
2289 var target = this.getEl();
2291 //this.handleSelection(e);
2296 nodes: this.getEl(),
2301 dragData.ddel = target.dom ; // the div element
2302 Roo.log(target.getWidth( ));
2303 dragData.ddel.style.width = target.getWidth() + 'px';
2310 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2311 * whole Element becomes the target, and this causes the drop gesture to append.
2313 getTargetFromEvent : function(e, dragged_card_el)
2315 var target = e.getTarget();
2316 while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2317 target = target.parentNode;
2328 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2329 // see if target is one of the 'cards'...
2332 //Roo.log(this.items.length);
2335 var last_card_n = 0;
2337 for (var i = 0;i< this.items.length;i++) {
2339 if (!this.items[i].el.hasClass('card')) {
2342 pos = this.getDropPoint(e, this.items[i].el.dom);
2344 cards_len = ret.cards.length;
2345 //Roo.log(this.items[i].el.dom.id);
2346 ret.cards.push(this.items[i]);
2348 if (ret.card_n < 0 && pos == 'above') {
2349 ret.position = cards_len > 0 ? 'below' : pos;
2350 ret.items_n = i > 0 ? i - 1 : 0;
2351 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2352 ret.card = ret.cards[ret.card_n];
2355 if (!ret.cards.length) {
2357 ret.position = 'below';
2361 // could not find a card.. stick it at the end..
2362 if (ret.card_n < 0) {
2363 ret.card_n = last_card_n;
2364 ret.card = ret.cards[last_card_n];
2365 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2366 ret.position = 'below';
2369 if (this.items[ret.items_n].el == dragged_card_el) {
2373 if (ret.position == 'below') {
2374 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2376 if (card_after && card_after.el == dragged_card_el) {
2383 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2385 if (card_before && card_before.el == dragged_card_el) {
2392 onNodeEnter : function(n, dd, e, data){
2395 onNodeOver : function(n, dd, e, data)
2398 var target_info = this.getTargetFromEvent(e,data.source.el);
2399 if (target_info === false) {
2400 this.dropPlaceHolder('hide');
2403 Roo.log(['getTargetFromEvent', target_info ]);
2406 this.dropPlaceHolder('show', target_info,data);
2410 onNodeOut : function(n, dd, e, data){
2411 this.dropPlaceHolder('hide');
2414 onNodeDrop : function(n, dd, e, data)
2417 // call drop - return false if
2419 // this could actually fail - if the Network drops..
2420 // we will ignore this at present..- client should probably reload
2421 // the whole set of cards if stuff like that fails.
2424 var info = this.getTargetFromEvent(e,data.source.el);
2425 if (info === false) {
2429 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2433 this.dropPlaceHolder('hide');
2435 // do the dom manipulation first..
2436 var dom = data.source.el.dom;
2437 dom.parentNode.removeChild(dom);
2440 if (info.card !== true) {
2441 var cardel = info.card.el.dom;
2443 if (info.position == 'above') {
2444 cardel.parentNode.insertBefore(dom, cardel);
2445 } else if (cardel.nextSibling) {
2446 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2448 cardel.parentNode.append(dom);
2451 // card container???
2452 this.bodyEl.dom.append(dom);
2455 //FIXME HANDLE card = true
2457 // add this to the correct place in items.
2461 // remove Card from items.
2463 var old_parent = data.source.parent();
2465 old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2467 if (this.items.length) {
2469 //Roo.log([info.items_n, info.position, this.items.length]);
2470 for (var i =0; i < this.items.length; i++) {
2471 if (i == info.items_n && info.position == 'above') {
2472 nitems.push(data.source);
2474 nitems.push(this.items[i]);
2475 if (i == info.items_n && info.position == 'below') {
2476 nitems.push(data.source);
2479 this.items = nitems;
2480 Roo.log(this.items);
2482 this.items.push(data.source);
2485 data.source.parentId = this.id;
2490 /** Decide whether to drop above or below a View node. */
2491 getDropPoint : function(e, n, dd)
2496 if (n == this.bodyEl.dom) {
2499 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2500 var c = t + (b - t) / 2;
2501 var y = Roo.lib.Event.getPageY(e);
2508 onToggleCollapse : function(e)
2510 if (this.collapsed) {
2511 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2512 this.collapsableEl.addClass('show');
2513 this.collapsed = false;
2516 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2517 this.collapsableEl.removeClass('show');
2518 this.collapsed = true;
2523 onToggleRotate : function(e)
2525 this.collapsableEl.removeClass('show');
2526 this.footerEl.removeClass('d-none');
2527 this.el.removeClass('roo-card-rotated');
2528 this.el.removeClass('d-none');
2531 this.collapsableEl.addClass('show');
2532 this.rotated = false;
2533 this.fireEvent('rotate', this, this.rotated);
2536 this.el.addClass('roo-card-rotated');
2537 this.footerEl.addClass('d-none');
2538 this.el.select('.roo-collapsable').removeClass('show');
2540 this.rotated = true;
2541 this.fireEvent('rotate', this, this.rotated);
2545 dropPlaceHolder: function (action, info, data)
2547 if (this.dropEl === false) {
2548 this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2552 this.dropEl.removeClass(['d-none', 'd-block']);
2553 if (action == 'hide') {
2555 this.dropEl.addClass('d-none');
2558 // FIXME - info.card == true!!!
2559 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2561 if (info.card !== true) {
2562 var cardel = info.card.el.dom;
2564 if (info.position == 'above') {
2565 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2566 } else if (cardel.nextSibling) {
2567 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2569 cardel.parentNode.append(this.dropEl.dom);
2572 // card container???
2573 this.bodyEl.dom.append(this.dropEl.dom);
2576 this.dropEl.addClass('d-block roo-card-dropzone');
2578 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2585 setHeaderText: function(html)
2587 this.headerEl.dom.innerHTML = html;
2596 * Card header - holder for the card header elements.
2601 * @class Roo.bootstrap.CardHeader
2602 * @extends Roo.bootstrap.Element
2603 * Bootstrap CardHeader class
2605 * Create a new Card Header - that you can embed children into
2606 * @param {Object} config The config object
2609 Roo.bootstrap.CardHeader = function(config){
2610 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2613 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2616 container_method : 'getCardHeader'
2629 * Card footer - holder for the card footer elements.
2634 * @class Roo.bootstrap.CardFooter
2635 * @extends Roo.bootstrap.Element
2636 * Bootstrap CardFooter class
2638 * Create a new Card Footer - that you can embed children into
2639 * @param {Object} config The config object
2642 Roo.bootstrap.CardFooter = function(config){
2643 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2646 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2649 container_method : 'getCardFooter'
2662 * Card header - holder for the card header elements.
2667 * @class Roo.bootstrap.CardImageTop
2668 * @extends Roo.bootstrap.Element
2669 * Bootstrap CardImageTop class
2671 * Create a new Card Image Top container
2672 * @param {Object} config The config object
2675 Roo.bootstrap.CardImageTop = function(config){
2676 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2679 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2682 container_method : 'getCardImageTop'
2700 * @class Roo.bootstrap.Img
2701 * @extends Roo.bootstrap.Component
2702 * Bootstrap Img class
2703 * @cfg {Boolean} imgResponsive false | true
2704 * @cfg {String} border rounded | circle | thumbnail
2705 * @cfg {String} src image source
2706 * @cfg {String} alt image alternative text
2707 * @cfg {String} href a tag href
2708 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2709 * @cfg {String} xsUrl xs image source
2710 * @cfg {String} smUrl sm image source
2711 * @cfg {String} mdUrl md image source
2712 * @cfg {String} lgUrl lg image source
2715 * Create a new Input
2716 * @param {Object} config The config object
2719 Roo.bootstrap.Img = function(config){
2720 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2726 * The img click event for the img.
2727 * @param {Roo.EventObject} e
2733 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2735 imgResponsive: true,
2745 getAutoCreate : function()
2747 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2748 return this.createSingleImg();
2753 cls: 'roo-image-responsive-group',
2758 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2760 if(!_this[size + 'Url']){
2766 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2767 html: _this.html || cfg.html,
2768 src: _this[size + 'Url']
2771 img.cls += ' roo-image-responsive-' + size;
2773 var s = ['xs', 'sm', 'md', 'lg'];
2775 s.splice(s.indexOf(size), 1);
2777 Roo.each(s, function(ss){
2778 img.cls += ' hidden-' + ss;
2781 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2782 cfg.cls += ' img-' + _this.border;
2786 cfg.alt = _this.alt;
2799 a.target = _this.target;
2803 cfg.cn.push((_this.href) ? a : img);
2810 createSingleImg : function()
2814 cls: (this.imgResponsive) ? 'img-responsive' : '',
2816 src : 'about:blank' // just incase src get's set to undefined?!?
2819 cfg.html = this.html || cfg.html;
2821 cfg.src = this.src || cfg.src;
2823 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2824 cfg.cls += ' img-' + this.border;
2841 a.target = this.target;
2846 return (this.href) ? a : cfg;
2849 initEvents: function()
2852 this.el.on('click', this.onClick, this);
2857 onClick : function(e)
2859 Roo.log('img onclick');
2860 this.fireEvent('click', this, e);
2863 * Sets the url of the image - used to update it
2864 * @param {String} url the url of the image
2867 setSrc : function(url)
2871 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2872 this.el.dom.src = url;
2876 this.el.select('img', true).first().dom.src = url;
2892 * @class Roo.bootstrap.Link
2893 * @extends Roo.bootstrap.Component
2894 * Bootstrap Link Class
2895 * @cfg {String} alt image alternative text
2896 * @cfg {String} href a tag href
2897 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2898 * @cfg {String} html the content of the link.
2899 * @cfg {String} anchor name for the anchor link
2900 * @cfg {String} fa - favicon
2902 * @cfg {Boolean} preventDefault (true | false) default false
2906 * Create a new Input
2907 * @param {Object} config The config object
2910 Roo.bootstrap.Link = function(config){
2911 Roo.bootstrap.Link.superclass.constructor.call(this, config);
2917 * The img click event for the img.
2918 * @param {Roo.EventObject} e
2924 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
2928 preventDefault: false,
2934 getAutoCreate : function()
2936 var html = this.html || '';
2938 if (this.fa !== false) {
2939 html = '<i class="fa fa-' + this.fa + '"></i>';
2944 // anchor's do not require html/href...
2945 if (this.anchor === false) {
2947 cfg.href = this.href || '#';
2949 cfg.name = this.anchor;
2950 if (this.html !== false || this.fa !== false) {
2953 if (this.href !== false) {
2954 cfg.href = this.href;
2958 if(this.alt !== false){
2963 if(this.target !== false) {
2964 cfg.target = this.target;
2970 initEvents: function() {
2972 if(!this.href || this.preventDefault){
2973 this.el.on('click', this.onClick, this);
2977 onClick : function(e)
2979 if(this.preventDefault){
2982 //Roo.log('img onclick');
2983 this.fireEvent('click', this, e);
2996 * @class Roo.bootstrap.Header
2997 * @extends Roo.bootstrap.Component
2998 * Bootstrap Header class
2999 * @cfg {String} html content of header
3000 * @cfg {Number} level (1|2|3|4|5|6) default 1
3003 * Create a new Header
3004 * @param {Object} config The config object
3008 Roo.bootstrap.Header = function(config){
3009 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3012 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3020 getAutoCreate : function(){
3025 tag: 'h' + (1 *this.level),
3026 html: this.html || ''
3038 * Ext JS Library 1.1.1
3039 * Copyright(c) 2006-2007, Ext JS, LLC.
3041 * Originally Released Under LGPL - original licence link has changed is not relivant.
3044 * <script type="text/javascript">
3048 * @class Roo.bootstrap.MenuMgr
3049 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3052 Roo.bootstrap.MenuMgr = function(){
3053 var menus, active, groups = {}, attached = false, lastShow = new Date();
3055 // private - called when first menu is created
3058 active = new Roo.util.MixedCollection();
3059 Roo.get(document).addKeyListener(27, function(){
3060 if(active.length > 0){
3068 if(active && active.length > 0){
3069 var c = active.clone();
3079 if(active.length < 1){
3080 Roo.get(document).un("mouseup", onMouseDown);
3088 var last = active.last();
3089 lastShow = new Date();
3092 Roo.get(document).on("mouseup", onMouseDown);
3097 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3098 m.parentMenu.activeChild = m;
3099 }else if(last && last.isVisible()){
3100 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3105 function onBeforeHide(m){
3107 m.activeChild.hide();
3109 if(m.autoHideTimer){
3110 clearTimeout(m.autoHideTimer);
3111 delete m.autoHideTimer;
3116 function onBeforeShow(m){
3117 var pm = m.parentMenu;
3118 if(!pm && !m.allowOtherMenus){
3120 }else if(pm && pm.activeChild && active != m){
3121 pm.activeChild.hide();
3125 // private this should really trigger on mouseup..
3126 function onMouseDown(e){
3127 Roo.log("on Mouse Up");
3129 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3130 Roo.log("MenuManager hideAll");
3139 function onBeforeCheck(mi, state){
3141 var g = groups[mi.group];
3142 for(var i = 0, l = g.length; i < l; i++){
3144 g[i].setChecked(false);
3153 * Hides all menus that are currently visible
3155 hideAll : function(){
3160 register : function(menu){
3164 menus[menu.id] = menu;
3165 menu.on("beforehide", onBeforeHide);
3166 menu.on("hide", onHide);
3167 menu.on("beforeshow", onBeforeShow);
3168 menu.on("show", onShow);
3170 if(g && menu.events["checkchange"]){
3174 groups[g].push(menu);
3175 menu.on("checkchange", onCheck);
3180 * Returns a {@link Roo.menu.Menu} object
3181 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3182 * be used to generate and return a new Menu instance.
3184 get : function(menu){
3185 if(typeof menu == "string"){ // menu id
3187 }else if(menu.events){ // menu instance
3190 /*else if(typeof menu.length == 'number'){ // array of menu items?
3191 return new Roo.bootstrap.Menu({items:menu});
3192 }else{ // otherwise, must be a config
3193 return new Roo.bootstrap.Menu(menu);
3200 unregister : function(menu){
3201 delete menus[menu.id];
3202 menu.un("beforehide", onBeforeHide);
3203 menu.un("hide", onHide);
3204 menu.un("beforeshow", onBeforeShow);
3205 menu.un("show", onShow);
3207 if(g && menu.events["checkchange"]){
3208 groups[g].remove(menu);
3209 menu.un("checkchange", onCheck);
3214 registerCheckable : function(menuItem){
3215 var g = menuItem.group;
3220 groups[g].push(menuItem);
3221 menuItem.on("beforecheckchange", onBeforeCheck);
3226 unregisterCheckable : function(menuItem){
3227 var g = menuItem.group;
3229 groups[g].remove(menuItem);
3230 menuItem.un("beforecheckchange", onBeforeCheck);
3242 * @class Roo.bootstrap.Menu
3243 * @extends Roo.bootstrap.Component
3244 * Bootstrap Menu class - container for MenuItems
3245 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3246 * @cfg {bool} hidden if the menu should be hidden when rendered.
3247 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3248 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3252 * @param {Object} config The config object
3256 Roo.bootstrap.Menu = function(config){
3257 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3258 if (this.registerMenu && this.type != 'treeview') {
3259 Roo.bootstrap.MenuMgr.register(this);
3266 * Fires before this menu is displayed (return false to block)
3267 * @param {Roo.menu.Menu} this
3272 * Fires before this menu is hidden (return false to block)
3273 * @param {Roo.menu.Menu} this
3278 * Fires after this menu is displayed
3279 * @param {Roo.menu.Menu} this
3284 * Fires after this menu is hidden
3285 * @param {Roo.menu.Menu} this
3290 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3291 * @param {Roo.menu.Menu} this
3292 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3293 * @param {Roo.EventObject} e
3298 * Fires when the mouse is hovering over this menu
3299 * @param {Roo.menu.Menu} this
3300 * @param {Roo.EventObject} e
3301 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3306 * Fires when the mouse exits this menu
3307 * @param {Roo.menu.Menu} this
3308 * @param {Roo.EventObject} e
3309 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3314 * Fires when a menu item contained in this menu is clicked
3315 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3316 * @param {Roo.EventObject} e
3320 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3323 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3327 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3330 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3332 registerMenu : true,
3334 menuItems :false, // stores the menu items..
3344 getChildContainer : function() {
3348 getAutoCreate : function(){
3350 //if (['right'].indexOf(this.align)!==-1) {
3351 // cfg.cn[1].cls += ' pull-right'
3357 cls : 'dropdown-menu' ,
3358 style : 'z-index:1000'
3362 if (this.type === 'submenu') {
3363 cfg.cls = 'submenu active';
3365 if (this.type === 'treeview') {
3366 cfg.cls = 'treeview-menu';
3371 initEvents : function() {
3373 // Roo.log("ADD event");
3374 // Roo.log(this.triggerEl.dom);
3376 this.triggerEl.on('click', this.onTriggerClick, this);
3378 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3381 if (this.triggerEl.hasClass('nav-item')) {
3382 // dropdown toggle on the 'a' in BS4?
3383 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3385 this.triggerEl.addClass('dropdown-toggle');
3388 this.el.on('touchstart' , this.onTouch, this);
3390 this.el.on('click' , this.onClick, this);
3392 this.el.on("mouseover", this.onMouseOver, this);
3393 this.el.on("mouseout", this.onMouseOut, this);
3397 findTargetItem : function(e)
3399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3403 //Roo.log(t); Roo.log(t.id);
3405 //Roo.log(this.menuitems);
3406 return this.menuitems.get(t.id);
3408 //return this.items.get(t.menuItemId);
3414 onTouch : function(e)
3416 Roo.log("menu.onTouch");
3417 //e.stopEvent(); this make the user popdown broken
3421 onClick : function(e)
3423 Roo.log("menu.onClick");
3425 var t = this.findTargetItem(e);
3426 if(!t || t.isContainer){
3431 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3432 if(t == this.activeItem && t.shouldDeactivate(e)){
3433 this.activeItem.deactivate();
3434 delete this.activeItem;
3438 this.setActiveItem(t, true);
3446 Roo.log('pass click event');
3450 this.fireEvent("click", this, t, e);
3454 if(!t.href.length || t.href == '#'){
3455 (function() { _this.hide(); }).defer(100);
3460 onMouseOver : function(e){
3461 var t = this.findTargetItem(e);
3464 // if(t.canActivate && !t.disabled){
3465 // this.setActiveItem(t, true);
3469 this.fireEvent("mouseover", this, e, t);
3471 isVisible : function(){
3472 return !this.hidden;
3474 onMouseOut : function(e){
3475 var t = this.findTargetItem(e);
3478 // if(t == this.activeItem && t.shouldDeactivate(e)){
3479 // this.activeItem.deactivate();
3480 // delete this.activeItem;
3483 this.fireEvent("mouseout", this, e, t);
3488 * Displays this menu relative to another element
3489 * @param {String/HTMLElement/Roo.Element} element The element to align to
3490 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3491 * the element (defaults to this.defaultAlign)
3492 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3494 show : function(el, pos, parentMenu)
3496 if (false === this.fireEvent("beforeshow", this)) {
3497 Roo.log("show canceled");
3500 this.parentMenu = parentMenu;
3505 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3508 * Displays this menu at a specific xy position
3509 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3510 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3512 showAt : function(xy, parentMenu, /* private: */_e){
3513 this.parentMenu = parentMenu;
3518 this.fireEvent("beforeshow", this);
3519 //xy = this.el.adjustForConstraints(xy);
3523 this.hideMenuItems();
3524 this.hidden = false;
3525 this.triggerEl.addClass('open');
3526 this.el.addClass('show');
3528 // reassign x when hitting right
3529 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3530 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3533 // reassign y when hitting bottom
3534 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3535 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3538 // but the list may align on trigger left or trigger top... should it be a properity?
3540 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3545 this.fireEvent("show", this);
3551 this.doFocus.defer(50, this);
3555 doFocus : function(){
3557 this.focusEl.focus();
3562 * Hides this menu and optionally all parent menus
3563 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3565 hide : function(deep)
3567 if (false === this.fireEvent("beforehide", this)) {
3568 Roo.log("hide canceled");
3571 this.hideMenuItems();
3572 if(this.el && this.isVisible()){
3574 if(this.activeItem){
3575 this.activeItem.deactivate();
3576 this.activeItem = null;
3578 this.triggerEl.removeClass('open');;
3579 this.el.removeClass('show');
3581 this.fireEvent("hide", this);
3583 if(deep === true && this.parentMenu){
3584 this.parentMenu.hide(true);
3588 onTriggerClick : function(e)
3590 Roo.log('trigger click');
3592 var target = e.getTarget();
3594 Roo.log(target.nodeName.toLowerCase());
3596 if(target.nodeName.toLowerCase() === 'i'){
3602 onTriggerPress : function(e)
3604 Roo.log('trigger press');
3605 //Roo.log(e.getTarget());
3606 // Roo.log(this.triggerEl.dom);
3608 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3609 var pel = Roo.get(e.getTarget());
3610 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3611 Roo.log('is treeview or dropdown?');
3615 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3619 if (this.isVisible()) {
3624 this.show(this.triggerEl, '?', false);
3627 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3634 hideMenuItems : function()
3636 Roo.log("hide Menu Items");
3641 this.el.select('.open',true).each(function(aa) {
3643 aa.removeClass('open');
3647 addxtypeChild : function (tree, cntr) {
3648 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3650 this.menuitems.add(comp);
3662 this.getEl().dom.innerHTML = '';
3663 this.menuitems.clear();
3677 * @class Roo.bootstrap.MenuItem
3678 * @extends Roo.bootstrap.Component
3679 * Bootstrap MenuItem class
3680 * @cfg {String} html the menu label
3681 * @cfg {String} href the link
3682 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3683 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3684 * @cfg {Boolean} active used on sidebars to highlight active itesm
3685 * @cfg {String} fa favicon to show on left of menu item.
3686 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3690 * Create a new MenuItem
3691 * @param {Object} config The config object
3695 Roo.bootstrap.MenuItem = function(config){
3696 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3701 * The raw click event for the entire grid.
3702 * @param {Roo.bootstrap.MenuItem} this
3703 * @param {Roo.EventObject} e
3709 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3713 preventDefault: false,
3714 isContainer : false,
3718 getAutoCreate : function(){
3720 if(this.isContainer){
3723 cls: 'dropdown-menu-item '
3733 cls : 'dropdown-item',
3738 if (this.fa !== false) {
3741 cls : 'fa fa-' + this.fa
3750 cls: 'dropdown-menu-item',
3753 if (this.parent().type == 'treeview') {
3754 cfg.cls = 'treeview-menu';
3757 cfg.cls += ' active';
3762 anc.href = this.href || cfg.cn[0].href ;
3763 ctag.html = this.html || cfg.cn[0].html ;
3767 initEvents: function()
3769 if (this.parent().type == 'treeview') {
3770 this.el.select('a').on('click', this.onClick, this);
3774 this.menu.parentType = this.xtype;
3775 this.menu.triggerEl = this.el;
3776 this.menu = this.addxtype(Roo.apply({}, this.menu));
3780 onClick : function(e)
3782 Roo.log('item on click ');
3784 if(this.preventDefault){
3787 //this.parent().hideMenuItems();
3789 this.fireEvent('click', this, e);
3808 * @class Roo.bootstrap.MenuSeparator
3809 * @extends Roo.bootstrap.Component
3810 * Bootstrap MenuSeparator class
3813 * Create a new MenuItem
3814 * @param {Object} config The config object
3818 Roo.bootstrap.MenuSeparator = function(config){
3819 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3822 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3824 getAutoCreate : function(){
3843 * @class Roo.bootstrap.Modal
3844 * @extends Roo.bootstrap.Component
3845 * Bootstrap Modal class
3846 * @cfg {String} title Title of dialog
3847 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3848 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3849 * @cfg {Boolean} specificTitle default false
3850 * @cfg {Array} buttons Array of buttons or standard button set..
3851 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3852 * @cfg {Boolean} animate default true
3853 * @cfg {Boolean} allow_close default true
3854 * @cfg {Boolean} fitwindow default false
3855 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3856 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3857 * @cfg {String} size (sm|lg|xl) default empty
3858 * @cfg {Number} max_width set the max width of modal
3859 * @cfg {Boolean} editableTitle can the title be edited
3864 * Create a new Modal Dialog
3865 * @param {Object} config The config object
3868 Roo.bootstrap.Modal = function(config){
3869 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3874 * The raw btnclick event for the button
3875 * @param {Roo.EventObject} e
3880 * Fire when dialog resize
3881 * @param {Roo.bootstrap.Modal} this
3882 * @param {Roo.EventObject} e
3886 * @event titlechanged
3887 * Fire when the editable title has been changed
3888 * @param {Roo.bootstrap.Modal} this
3889 * @param {Roo.EventObject} value
3891 "titlechanged" : true
3894 this.buttons = this.buttons || [];
3897 this.tmpl = Roo.factory(this.tmpl);
3902 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3904 title : 'test dialog',
3914 specificTitle: false,
3916 buttonPosition: 'right',
3938 editableTitle : false,
3940 onRender : function(ct, position)
3942 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3945 var cfg = Roo.apply({}, this.getAutoCreate());
3948 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3950 //if (!cfg.name.length) {
3954 cfg.cls += ' ' + this.cls;
3957 cfg.style = this.style;
3959 this.el = Roo.get(document.body).createChild(cfg, position);
3961 //var type = this.el.dom.type;
3964 if(this.tabIndex !== undefined){
3965 this.el.dom.setAttribute('tabIndex', this.tabIndex);
3968 this.dialogEl = this.el.select('.modal-dialog',true).first();
3969 this.bodyEl = this.el.select('.modal-body',true).first();
3970 this.closeEl = this.el.select('.modal-header .close', true).first();
3971 this.headerEl = this.el.select('.modal-header',true).first();
3972 this.titleEl = this.el.select('.modal-title',true).first();
3973 this.footerEl = this.el.select('.modal-footer',true).first();
3975 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3977 //this.el.addClass("x-dlg-modal");
3979 if (this.buttons.length) {
3980 Roo.each(this.buttons, function(bb) {
3981 var b = Roo.apply({}, bb);
3982 b.xns = b.xns || Roo.bootstrap;
3983 b.xtype = b.xtype || 'Button';
3984 if (typeof(b.listeners) == 'undefined') {
3985 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3988 var btn = Roo.factory(b);
3990 btn.render(this.getButtonContainer());
3994 // render the children.
3997 if(typeof(this.items) != 'undefined'){
3998 var items = this.items;
4001 for(var i =0;i < items.length;i++) {
4002 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4006 this.items = nitems;
4008 // where are these used - they used to be body/close/footer
4012 //this.el.addClass([this.fieldClass, this.cls]);
4016 getAutoCreate : function()
4018 // we will default to modal-body-overflow - might need to remove or make optional later.
4020 cls : 'modal-body enable-modal-body-overflow ',
4021 html : this.html || ''
4026 cls : 'modal-title',
4030 if(this.specificTitle){ // WTF is this?
4035 if (this.allow_close && Roo.bootstrap.version == 3) {
4045 if (this.editableTitle) {
4047 cls: 'form-control roo-editable-title d-none',
4053 if (this.allow_close && Roo.bootstrap.version == 4) {
4063 if(this.size.length){
4064 size = 'modal-' + this.size;
4067 var footer = Roo.bootstrap.version == 3 ?
4069 cls : 'modal-footer',
4073 cls: 'btn-' + this.buttonPosition
4078 { // BS4 uses mr-auto on left buttons....
4079 cls : 'modal-footer'
4090 cls: "modal-dialog " + size,
4093 cls : "modal-content",
4096 cls : 'modal-header',
4111 modal.cls += ' fade';
4117 getChildContainer : function() {
4122 getButtonContainer : function() {
4124 return Roo.bootstrap.version == 4 ?
4125 this.el.select('.modal-footer',true).first()
4126 : this.el.select('.modal-footer div',true).first();
4129 initEvents : function()
4131 if (this.allow_close) {
4132 this.closeEl.on('click', this.hide, this);
4134 Roo.EventManager.onWindowResize(this.resize, this, true);
4135 if (this.editableTitle) {
4136 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4137 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4138 this.headerEditEl.on('keyup', function(e) {
4139 if(e.isNavKeyPress()){
4140 this.toggleHeaderInput(false)
4143 this.headerEditEl.on('blur', function(e) {
4144 this.toggleHeaderInput(false)
4153 this.maskEl.setSize(
4154 Roo.lib.Dom.getViewWidth(true),
4155 Roo.lib.Dom.getViewHeight(true)
4158 if (this.fitwindow) {
4162 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4163 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4168 if(this.max_width !== 0) {
4170 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4173 this.setSize(w, this.height);
4177 if(this.max_height) {
4178 this.setSize(w,Math.min(
4180 Roo.lib.Dom.getViewportHeight(true) - 60
4186 if(!this.fit_content) {
4187 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4191 this.setSize(w, Math.min(
4193 this.headerEl.getHeight() +
4194 this.footerEl.getHeight() +
4195 this.getChildHeight(this.bodyEl.dom.childNodes),
4196 Roo.lib.Dom.getViewportHeight(true) - 60)
4202 setSize : function(w,h)
4213 if (!this.rendered) {
4217 //this.el.setStyle('display', 'block');
4218 this.el.removeClass('hideing');
4219 this.el.dom.style.display='block';
4221 Roo.get(document.body).addClass('modal-open');
4223 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4226 this.el.addClass('show');
4227 this.el.addClass('in');
4230 this.el.addClass('show');
4231 this.el.addClass('in');
4234 // not sure how we can show data in here..
4236 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4239 Roo.get(document.body).addClass("x-body-masked");
4241 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4242 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4243 this.maskEl.dom.style.display = 'block';
4244 this.maskEl.addClass('show');
4249 this.fireEvent('show', this);
4251 // set zindex here - otherwise it appears to be ignored...
4252 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4255 this.items.forEach( function(e) {
4256 e.layout ? e.layout() : false;
4264 if(this.fireEvent("beforehide", this) !== false){
4266 this.maskEl.removeClass('show');
4268 this.maskEl.dom.style.display = '';
4269 Roo.get(document.body).removeClass("x-body-masked");
4270 this.el.removeClass('in');
4271 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4273 if(this.animate){ // why
4274 this.el.addClass('hideing');
4275 this.el.removeClass('show');
4277 if (!this.el.hasClass('hideing')) {
4278 return; // it's been shown again...
4281 this.el.dom.style.display='';
4283 Roo.get(document.body).removeClass('modal-open');
4284 this.el.removeClass('hideing');
4288 this.el.removeClass('show');
4289 this.el.dom.style.display='';
4290 Roo.get(document.body).removeClass('modal-open');
4293 this.fireEvent('hide', this);
4296 isVisible : function()
4299 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4303 addButton : function(str, cb)
4307 var b = Roo.apply({}, { html : str } );
4308 b.xns = b.xns || Roo.bootstrap;
4309 b.xtype = b.xtype || 'Button';
4310 if (typeof(b.listeners) == 'undefined') {
4311 b.listeners = { click : cb.createDelegate(this) };
4314 var btn = Roo.factory(b);
4316 btn.render(this.getButtonContainer());
4322 setDefaultButton : function(btn)
4324 //this.el.select('.modal-footer').()
4327 resizeTo: function(w,h)
4329 this.dialogEl.setWidth(w);
4331 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4333 this.bodyEl.setHeight(h - diff);
4335 this.fireEvent('resize', this);
4338 setContentSize : function(w, h)
4342 onButtonClick: function(btn,e)
4345 this.fireEvent('btnclick', btn.name, e);
4348 * Set the title of the Dialog
4349 * @param {String} str new Title
4351 setTitle: function(str) {
4352 this.titleEl.dom.innerHTML = str;
4356 * Set the body of the Dialog
4357 * @param {String} str new Title
4359 setBody: function(str) {
4360 this.bodyEl.dom.innerHTML = str;
4363 * Set the body of the Dialog using the template
4364 * @param {Obj} data - apply this data to the template and replace the body contents.
4366 applyBody: function(obj)
4369 Roo.log("Error - using apply Body without a template");
4372 this.tmpl.overwrite(this.bodyEl, obj);
4375 getChildHeight : function(child_nodes)
4379 child_nodes.length == 0
4384 var child_height = 0;
4386 for(var i = 0; i < child_nodes.length; i++) {
4389 * for modal with tabs...
4390 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4392 var layout_childs = child_nodes[i].childNodes;
4394 for(var j = 0; j < layout_childs.length; j++) {
4396 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4398 var layout_body_childs = layout_childs[j].childNodes;
4400 for(var k = 0; k < layout_body_childs.length; k++) {
4402 if(layout_body_childs[k].classList.contains('navbar')) {
4403 child_height += layout_body_childs[k].offsetHeight;
4407 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4409 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4411 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4413 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4414 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4429 child_height += child_nodes[i].offsetHeight;
4430 // Roo.log(child_nodes[i].offsetHeight);
4433 return child_height;
4435 toggleHeaderInput : function(is_edit)
4438 if (is_edit && this.is_header_editing) {
4439 return; // already editing..
4443 this.headerEditEl.dom.value = this.title;
4444 this.headerEditEl.removeClass('d-none');
4445 this.headerEditEl.dom.focus();
4446 this.titleEl.addClass('d-none');
4448 this.is_header_editing = true;
4451 // flip back to not editing.
4452 this.title = this.headerEditEl.dom.value;
4453 this.headerEditEl.addClass('d-none');
4454 this.titleEl.removeClass('d-none');
4455 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4456 this.is_header_editing = false;
4457 this.fireEvent('titlechanged', this, this.title);
4466 Roo.apply(Roo.bootstrap.Modal, {
4468 * Button config that displays a single OK button
4477 * Button config that displays Yes and No buttons
4493 * Button config that displays OK and Cancel buttons
4508 * Button config that displays Yes, No and Cancel buttons
4533 * messagebox - can be used as a replace
4537 * @class Roo.MessageBox
4538 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4542 Roo.Msg.alert('Status', 'Changes saved successfully.');
4544 // Prompt for user data:
4545 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4547 // process text value...
4551 // Show a dialog using config options:
4553 title:'Save Changes?',
4554 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4555 buttons: Roo.Msg.YESNOCANCEL,
4562 Roo.bootstrap.MessageBox = function(){
4563 var dlg, opt, mask, waitTimer;
4564 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4565 var buttons, activeTextEl, bwidth;
4569 var handleButton = function(button){
4571 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4575 var handleHide = function(){
4577 dlg.el.removeClass(opt.cls);
4580 // Roo.TaskMgr.stop(waitTimer);
4581 // waitTimer = null;
4586 var updateButtons = function(b){
4589 buttons["ok"].hide();
4590 buttons["cancel"].hide();
4591 buttons["yes"].hide();
4592 buttons["no"].hide();
4593 dlg.footerEl.hide();
4597 dlg.footerEl.show();
4598 for(var k in buttons){
4599 if(typeof buttons[k] != "function"){
4602 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4603 width += buttons[k].el.getWidth()+15;
4613 var handleEsc = function(d, k, e){
4614 if(opt && opt.closable !== false){
4624 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4625 * @return {Roo.BasicDialog} The BasicDialog element
4627 getDialog : function(){
4629 dlg = new Roo.bootstrap.Modal( {
4632 //constraintoviewport:false,
4634 //collapsible : false,
4639 //buttonAlign:"center",
4640 closeClick : function(){
4641 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4644 handleButton("cancel");
4649 dlg.on("hide", handleHide);
4651 //dlg.addKeyListener(27, handleEsc);
4653 this.buttons = buttons;
4654 var bt = this.buttonText;
4655 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4656 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4657 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4658 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4660 bodyEl = dlg.bodyEl.createChild({
4662 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4663 '<textarea class="roo-mb-textarea"></textarea>' +
4664 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4666 msgEl = bodyEl.dom.firstChild;
4667 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4668 textboxEl.enableDisplayMode();
4669 textboxEl.addKeyListener([10,13], function(){
4670 if(dlg.isVisible() && opt && opt.buttons){
4673 }else if(opt.buttons.yes){
4674 handleButton("yes");
4678 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4679 textareaEl.enableDisplayMode();
4680 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4681 progressEl.enableDisplayMode();
4683 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4684 var pf = progressEl.dom.firstChild;
4686 pp = Roo.get(pf.firstChild);
4687 pp.setHeight(pf.offsetHeight);
4695 * Updates the message box body text
4696 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4697 * the XHTML-compliant non-breaking space character '&#160;')
4698 * @return {Roo.MessageBox} This message box
4700 updateText : function(text)
4702 if(!dlg.isVisible() && !opt.width){
4703 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4704 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4706 msgEl.innerHTML = text || ' ';
4708 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4709 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4711 Math.min(opt.width || cw , this.maxWidth),
4712 Math.max(opt.minWidth || this.minWidth, bwidth)
4715 activeTextEl.setWidth(w);
4717 if(dlg.isVisible()){
4718 dlg.fixedcenter = false;
4720 // to big, make it scroll. = But as usual stupid IE does not support
4723 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4724 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4725 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4727 bodyEl.dom.style.height = '';
4728 bodyEl.dom.style.overflowY = '';
4731 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4733 bodyEl.dom.style.overflowX = '';
4736 dlg.setContentSize(w, bodyEl.getHeight());
4737 if(dlg.isVisible()){
4738 dlg.fixedcenter = true;
4744 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4745 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4746 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4747 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4748 * @return {Roo.MessageBox} This message box
4750 updateProgress : function(value, text){
4752 this.updateText(text);
4755 if (pp) { // weird bug on my firefox - for some reason this is not defined
4756 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4757 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4763 * Returns true if the message box is currently displayed
4764 * @return {Boolean} True if the message box is visible, else false
4766 isVisible : function(){
4767 return dlg && dlg.isVisible();
4771 * Hides the message box if it is displayed
4774 if(this.isVisible()){
4780 * Displays a new message box, or reinitializes an existing message box, based on the config options
4781 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4782 * The following config object properties are supported:
4784 Property Type Description
4785 ---------- --------------- ------------------------------------------------------------------------------------
4786 animEl String/Element An id or Element from which the message box should animate as it opens and
4787 closes (defaults to undefined)
4788 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4789 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4790 closable Boolean False to hide the top-right close button (defaults to true). Note that
4791 progress and wait dialogs will ignore this property and always hide the
4792 close button as they can only be closed programmatically.
4793 cls String A custom CSS class to apply to the message box element
4794 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4795 displayed (defaults to 75)
4796 fn Function A callback function to execute after closing the dialog. The arguments to the
4797 function will be btn (the name of the button that was clicked, if applicable,
4798 e.g. "ok"), and text (the value of the active text field, if applicable).
4799 Progress and wait dialogs will ignore this option since they do not respond to
4800 user actions and can only be closed programmatically, so any required function
4801 should be called by the same code after it closes the dialog.
4802 icon String A CSS class that provides a background image to be used as an icon for
4803 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4804 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4805 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4806 modal Boolean False to allow user interaction with the page while the message box is
4807 displayed (defaults to true)
4808 msg String A string that will replace the existing message box body text (defaults
4809 to the XHTML-compliant non-breaking space character ' ')
4810 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4811 progress Boolean True to display a progress bar (defaults to false)
4812 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4813 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4814 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4815 title String The title text
4816 value String The string value to set into the active textbox element if displayed
4817 wait Boolean True to display a progress bar (defaults to false)
4818 width Number The width of the dialog in pixels
4825 msg: 'Please enter your address:',
4827 buttons: Roo.MessageBox.OKCANCEL,
4830 animEl: 'addAddressBtn'
4833 * @param {Object} config Configuration options
4834 * @return {Roo.MessageBox} This message box
4836 show : function(options)
4839 // this causes nightmares if you show one dialog after another
4840 // especially on callbacks..
4842 if(this.isVisible()){
4845 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4846 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4847 Roo.log("New Dialog Message:" + options.msg )
4848 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4849 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4852 var d = this.getDialog();
4854 d.setTitle(opt.title || " ");
4855 d.closeEl.setDisplayed(opt.closable !== false);
4856 activeTextEl = textboxEl;
4857 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4862 textareaEl.setHeight(typeof opt.multiline == "number" ?
4863 opt.multiline : this.defaultTextHeight);
4864 activeTextEl = textareaEl;
4873 progressEl.setDisplayed(opt.progress === true);
4875 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4877 this.updateProgress(0);
4878 activeTextEl.dom.value = opt.value || "";
4880 dlg.setDefaultButton(activeTextEl);
4882 var bs = opt.buttons;
4886 }else if(bs && bs.yes){
4887 db = buttons["yes"];
4889 dlg.setDefaultButton(db);
4891 bwidth = updateButtons(opt.buttons);
4892 this.updateText(opt.msg);
4894 d.el.addClass(opt.cls);
4896 d.proxyDrag = opt.proxyDrag === true;
4897 d.modal = opt.modal !== false;
4898 d.mask = opt.modal !== false ? mask : false;
4900 // force it to the end of the z-index stack so it gets a cursor in FF
4901 document.body.appendChild(dlg.el.dom);
4902 d.animateTarget = null;
4903 d.show(options.animEl);
4909 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4910 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4911 * and closing the message box when the process is complete.
4912 * @param {String} title The title bar text
4913 * @param {String} msg The message box body text
4914 * @return {Roo.MessageBox} This message box
4916 progress : function(title, msg){
4923 minWidth: this.minProgressWidth,
4930 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4931 * If a callback function is passed it will be called after the user clicks the button, and the
4932 * id of the button that was clicked will be passed as the only parameter to the callback
4933 * (could also be the top-right close button).
4934 * @param {String} title The title bar text
4935 * @param {String} msg The message box body text
4936 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4937 * @param {Object} scope (optional) The scope of the callback function
4938 * @return {Roo.MessageBox} This message box
4940 alert : function(title, msg, fn, scope)
4955 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
4956 * interaction while waiting for a long-running process to complete that does not have defined intervals.
4957 * You are responsible for closing the message box when the process is complete.
4958 * @param {String} msg The message box body text
4959 * @param {String} title (optional) The title bar text
4960 * @return {Roo.MessageBox} This message box
4962 wait : function(msg, title){
4973 waitTimer = Roo.TaskMgr.start({
4975 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4983 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4984 * If a callback function is passed it will be called after the user clicks either button, and the id of the
4985 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4986 * @param {String} title The title bar text
4987 * @param {String} msg The message box body text
4988 * @param {Function} fn (optional) The callback function invoked after the message box is closed
4989 * @param {Object} scope (optional) The scope of the callback function
4990 * @return {Roo.MessageBox} This message box
4992 confirm : function(title, msg, fn, scope){
4996 buttons: this.YESNO,
5005 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5006 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5007 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5008 * (could also be the top-right close button) and the text that was entered will be passed as the two
5009 * parameters to the callback.
5010 * @param {String} title The title bar text
5011 * @param {String} msg The message box body text
5012 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5013 * @param {Object} scope (optional) The scope of the callback function
5014 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5015 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5016 * @return {Roo.MessageBox} This message box
5018 prompt : function(title, msg, fn, scope, multiline){
5022 buttons: this.OKCANCEL,
5027 multiline: multiline,
5034 * Button config that displays a single OK button
5039 * Button config that displays Yes and No buttons
5042 YESNO : {yes:true, no:true},
5044 * Button config that displays OK and Cancel buttons
5047 OKCANCEL : {ok:true, cancel:true},
5049 * Button config that displays Yes, No and Cancel buttons
5052 YESNOCANCEL : {yes:true, no:true, cancel:true},
5055 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5058 defaultTextHeight : 75,
5060 * The maximum width in pixels of the message box (defaults to 600)
5065 * The minimum width in pixels of the message box (defaults to 100)
5070 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5071 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5074 minProgressWidth : 250,
5076 * An object containing the default button text strings that can be overriden for localized language support.
5077 * Supported properties are: ok, cancel, yes and no.
5078 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5091 * Shorthand for {@link Roo.MessageBox}
5093 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5094 Roo.Msg = Roo.Msg || Roo.MessageBox;
5103 * @class Roo.bootstrap.Navbar
5104 * @extends Roo.bootstrap.Component
5105 * Bootstrap Navbar class
5108 * Create a new Navbar
5109 * @param {Object} config The config object
5113 Roo.bootstrap.Navbar = function(config){
5114 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5118 * @event beforetoggle
5119 * Fire before toggle the menu
5120 * @param {Roo.EventObject} e
5122 "beforetoggle" : true
5126 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5135 getAutoCreate : function(){
5138 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5142 initEvents :function ()
5144 //Roo.log(this.el.select('.navbar-toggle',true));
5145 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5152 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5154 var size = this.el.getSize();
5155 this.maskEl.setSize(size.width, size.height);
5156 this.maskEl.enableDisplayMode("block");
5165 getChildContainer : function()
5167 if (this.el && this.el.select('.collapse').getCount()) {
5168 return this.el.select('.collapse',true).first();
5183 onToggle : function()
5186 if(this.fireEvent('beforetoggle', this) === false){
5189 var ce = this.el.select('.navbar-collapse',true).first();
5191 if (!ce.hasClass('show')) {
5201 * Expand the navbar pulldown
5203 expand : function ()
5206 var ce = this.el.select('.navbar-collapse',true).first();
5207 if (ce.hasClass('collapsing')) {
5210 ce.dom.style.height = '';
5212 ce.addClass('in'); // old...
5213 ce.removeClass('collapse');
5214 ce.addClass('show');
5215 var h = ce.getHeight();
5217 ce.removeClass('show');
5218 // at this point we should be able to see it..
5219 ce.addClass('collapsing');
5221 ce.setHeight(0); // resize it ...
5222 ce.on('transitionend', function() {
5223 //Roo.log('done transition');
5224 ce.removeClass('collapsing');
5225 ce.addClass('show');
5226 ce.removeClass('collapse');
5228 ce.dom.style.height = '';
5229 }, this, { single: true} );
5231 ce.dom.scrollTop = 0;
5234 * Collapse the navbar pulldown
5236 collapse : function()
5238 var ce = this.el.select('.navbar-collapse',true).first();
5240 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5241 // it's collapsed or collapsing..
5244 ce.removeClass('in'); // old...
5245 ce.setHeight(ce.getHeight());
5246 ce.removeClass('show');
5247 ce.addClass('collapsing');
5249 ce.on('transitionend', function() {
5250 ce.dom.style.height = '';
5251 ce.removeClass('collapsing');
5252 ce.addClass('collapse');
5253 }, this, { single: true} );
5273 * @class Roo.bootstrap.NavSimplebar
5274 * @extends Roo.bootstrap.Navbar
5275 * Bootstrap Sidebar class
5277 * @cfg {Boolean} inverse is inverted color
5279 * @cfg {String} type (nav | pills | tabs)
5280 * @cfg {Boolean} arrangement stacked | justified
5281 * @cfg {String} align (left | right) alignment
5283 * @cfg {Boolean} main (true|false) main nav bar? default false
5284 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5286 * @cfg {String} tag (header|footer|nav|div) default is nav
5288 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5292 * Create a new Sidebar
5293 * @param {Object} config The config object
5297 Roo.bootstrap.NavSimplebar = function(config){
5298 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5301 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5317 getAutoCreate : function(){
5321 tag : this.tag || 'div',
5322 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5324 if (['light','white'].indexOf(this.weight) > -1) {
5325 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5327 cfg.cls += ' bg-' + this.weight;
5330 cfg.cls += ' navbar-inverse';
5334 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5336 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5345 cls: 'nav nav-' + this.xtype,
5351 this.type = this.type || 'nav';
5352 if (['tabs','pills'].indexOf(this.type) != -1) {
5353 cfg.cn[0].cls += ' nav-' + this.type
5357 if (this.type!=='nav') {
5358 Roo.log('nav type must be nav/tabs/pills')
5360 cfg.cn[0].cls += ' navbar-nav'
5366 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5367 cfg.cn[0].cls += ' nav-' + this.arrangement;
5371 if (this.align === 'right') {
5372 cfg.cn[0].cls += ' navbar-right';
5397 * navbar-expand-md fixed-top
5401 * @class Roo.bootstrap.NavHeaderbar
5402 * @extends Roo.bootstrap.NavSimplebar
5403 * Bootstrap Sidebar class
5405 * @cfg {String} brand what is brand
5406 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5407 * @cfg {String} brand_href href of the brand
5408 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5409 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5410 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5411 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5414 * Create a new Sidebar
5415 * @param {Object} config The config object
5419 Roo.bootstrap.NavHeaderbar = function(config){
5420 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5424 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5431 desktopCenter : false,
5434 getAutoCreate : function(){
5437 tag: this.nav || 'nav',
5438 cls: 'navbar navbar-expand-md',
5444 if (this.desktopCenter) {
5445 cn.push({cls : 'container', cn : []});
5453 cls: 'navbar-toggle navbar-toggler',
5454 'data-toggle': 'collapse',
5459 html: 'Toggle navigation'
5463 cls: 'icon-bar navbar-toggler-icon'
5476 cn.push( Roo.bootstrap.version == 4 ? btn : {
5478 cls: 'navbar-header',
5487 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5491 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5493 if (['light','white'].indexOf(this.weight) > -1) {
5494 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5496 cfg.cls += ' bg-' + this.weight;
5499 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5500 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5502 // tag can override this..
5504 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5507 if (this.brand !== '') {
5508 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5509 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5511 href: this.brand_href ? this.brand_href : '#',
5512 cls: 'navbar-brand',
5520 cfg.cls += ' main-nav';
5528 getHeaderChildContainer : function()
5530 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5531 return this.el.select('.navbar-header',true).first();
5534 return this.getChildContainer();
5537 getChildContainer : function()
5540 return this.el.select('.roo-navbar-collapse',true).first();
5545 initEvents : function()
5547 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5549 if (this.autohide) {
5554 Roo.get(document).on('scroll',function(e) {
5555 var ns = Roo.get(document).getScroll().top;
5556 var os = prevScroll;
5560 ft.removeClass('slideDown');
5561 ft.addClass('slideUp');
5564 ft.removeClass('slideUp');
5565 ft.addClass('slideDown');
5586 * @class Roo.bootstrap.NavSidebar
5587 * @extends Roo.bootstrap.Navbar
5588 * Bootstrap Sidebar class
5591 * Create a new Sidebar
5592 * @param {Object} config The config object
5596 Roo.bootstrap.NavSidebar = function(config){
5597 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5600 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5602 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5604 getAutoCreate : function(){
5609 cls: 'sidebar sidebar-nav'
5631 * @class Roo.bootstrap.NavGroup
5632 * @extends Roo.bootstrap.Component
5633 * Bootstrap NavGroup class
5634 * @cfg {String} align (left|right)
5635 * @cfg {Boolean} inverse
5636 * @cfg {String} type (nav|pills|tab) default nav
5637 * @cfg {String} navId - reference Id for navbar.
5641 * Create a new nav group
5642 * @param {Object} config The config object
5645 Roo.bootstrap.NavGroup = function(config){
5646 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5649 Roo.bootstrap.NavGroup.register(this);
5653 * Fires when the active item changes
5654 * @param {Roo.bootstrap.NavGroup} this
5655 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5656 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5663 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5674 getAutoCreate : function()
5676 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5682 if (Roo.bootstrap.version == 4) {
5683 if (['tabs','pills'].indexOf(this.type) != -1) {
5684 cfg.cls += ' nav-' + this.type;
5686 // trying to remove so header bar can right align top?
5687 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5688 // do not use on header bar...
5689 cfg.cls += ' navbar-nav';
5694 if (['tabs','pills'].indexOf(this.type) != -1) {
5695 cfg.cls += ' nav-' + this.type
5697 if (this.type !== 'nav') {
5698 Roo.log('nav type must be nav/tabs/pills')
5700 cfg.cls += ' navbar-nav'
5704 if (this.parent() && this.parent().sidebar) {
5707 cls: 'dashboard-menu sidebar-menu'
5713 if (this.form === true) {
5716 cls: 'navbar-form form-inline'
5718 //nav navbar-right ml-md-auto
5719 if (this.align === 'right') {
5720 cfg.cls += ' navbar-right ml-md-auto';
5722 cfg.cls += ' navbar-left';
5726 if (this.align === 'right') {
5727 cfg.cls += ' navbar-right ml-md-auto';
5729 cfg.cls += ' mr-auto';
5733 cfg.cls += ' navbar-inverse';
5741 * sets the active Navigation item
5742 * @param {Roo.bootstrap.NavItem} the new current navitem
5744 setActiveItem : function(item)
5747 Roo.each(this.navItems, function(v){
5752 v.setActive(false, true);
5759 item.setActive(true, true);
5760 this.fireEvent('changed', this, item, prev);
5765 * gets the active Navigation item
5766 * @return {Roo.bootstrap.NavItem} the current navitem
5768 getActive : function()
5772 Roo.each(this.navItems, function(v){
5783 indexOfNav : function()
5787 Roo.each(this.navItems, function(v,i){
5798 * adds a Navigation item
5799 * @param {Roo.bootstrap.NavItem} the navitem to add
5801 addItem : function(cfg)
5803 if (this.form && Roo.bootstrap.version == 4) {
5806 var cn = new Roo.bootstrap.NavItem(cfg);
5808 cn.parentId = this.id;
5809 cn.onRender(this.el, null);
5813 * register a Navigation item
5814 * @param {Roo.bootstrap.NavItem} the navitem to add
5816 register : function(item)
5818 this.navItems.push( item);
5819 item.navId = this.navId;
5824 * clear all the Navigation item
5827 clearAll : function()
5830 this.el.dom.innerHTML = '';
5833 getNavItem: function(tabId)
5836 Roo.each(this.navItems, function(e) {
5837 if (e.tabId == tabId) {
5847 setActiveNext : function()
5849 var i = this.indexOfNav(this.getActive());
5850 if (i > this.navItems.length) {
5853 this.setActiveItem(this.navItems[i+1]);
5855 setActivePrev : function()
5857 var i = this.indexOfNav(this.getActive());
5861 this.setActiveItem(this.navItems[i-1]);
5863 clearWasActive : function(except) {
5864 Roo.each(this.navItems, function(e) {
5865 if (e.tabId != except.tabId && e.was_active) {
5866 e.was_active = false;
5873 getWasActive : function ()
5876 Roo.each(this.navItems, function(e) {
5891 Roo.apply(Roo.bootstrap.NavGroup, {
5895 * register a Navigation Group
5896 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5898 register : function(navgrp)
5900 this.groups[navgrp.navId] = navgrp;
5904 * fetch a Navigation Group based on the navigation ID
5905 * @param {string} the navgroup to add
5906 * @returns {Roo.bootstrap.NavGroup} the navgroup
5908 get: function(navId) {
5909 if (typeof(this.groups[navId]) == 'undefined') {
5911 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5913 return this.groups[navId] ;
5928 * @class Roo.bootstrap.NavItem
5929 * @extends Roo.bootstrap.Component
5930 * Bootstrap Navbar.NavItem class
5931 * @cfg {String} href link to
5932 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5934 * @cfg {String} html content of button
5935 * @cfg {String} badge text inside badge
5936 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5937 * @cfg {String} glyphicon DEPRICATED - use fa
5938 * @cfg {String} icon DEPRICATED - use fa
5939 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5940 * @cfg {Boolean} active Is item active
5941 * @cfg {Boolean} disabled Is item disabled
5943 * @cfg {Boolean} preventDefault (true | false) default false
5944 * @cfg {String} tabId the tab that this item activates.
5945 * @cfg {String} tagtype (a|span) render as a href or span?
5946 * @cfg {Boolean} animateRef (true|false) link to element default false
5949 * Create a new Navbar Item
5950 * @param {Object} config The config object
5952 Roo.bootstrap.NavItem = function(config){
5953 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5958 * The raw click event for the entire grid.
5959 * @param {Roo.EventObject} e
5964 * Fires when the active item active state changes
5965 * @param {Roo.bootstrap.NavItem} this
5966 * @param {boolean} state the new state
5972 * Fires when scroll to element
5973 * @param {Roo.bootstrap.NavItem} this
5974 * @param {Object} options
5975 * @param {Roo.EventObject} e
5983 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
5992 preventDefault : false,
6000 button_outline : false,
6004 getAutoCreate : function(){
6012 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6014 if (this.disabled) {
6015 cfg.cls += ' disabled';
6019 if (this.button_weight.length) {
6020 cfg.tag = this.href ? 'a' : 'button';
6021 cfg.html = this.html || '';
6022 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6024 cfg.href = this.href;
6027 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6030 // menu .. should add dropdown-menu class - so no need for carat..
6032 if (this.badge !== '') {
6034 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6039 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6043 href : this.href || "#",
6044 html: this.html || ''
6047 if (this.tagtype == 'a') {
6048 cfg.cn[0].cls = 'nav-link';
6051 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6054 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6056 if(this.glyphicon) {
6057 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6062 cfg.cn[0].html += " <span class='caret'></span>";
6066 if (this.badge !== '') {
6068 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6076 onRender : function(ct, position)
6078 // Roo.log("Call onRender: " + this.xtype);
6079 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6083 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6084 this.navLink = this.el.select('.nav-link',true).first();
6089 initEvents: function()
6091 if (typeof (this.menu) != 'undefined') {
6092 this.menu.parentType = this.xtype;
6093 this.menu.triggerEl = this.el;
6094 this.menu = this.addxtype(Roo.apply({}, this.menu));
6097 this.el.select('a',true).on('click', this.onClick, this);
6099 if(this.tagtype == 'span'){
6100 this.el.select('span',true).on('click', this.onClick, this);
6103 // at this point parent should be available..
6104 this.parent().register(this);
6107 onClick : function(e)
6109 if (e.getTarget('.dropdown-menu-item')) {
6110 // did you click on a menu itemm.... - then don't trigger onclick..
6115 this.preventDefault ||
6118 Roo.log("NavItem - prevent Default?");
6122 if (this.disabled) {
6126 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6127 if (tg && tg.transition) {
6128 Roo.log("waiting for the transitionend");
6134 //Roo.log("fire event clicked");
6135 if(this.fireEvent('click', this, e) === false){
6139 if(this.tagtype == 'span'){
6143 //Roo.log(this.href);
6144 var ael = this.el.select('a',true).first();
6147 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6148 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6149 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6150 return; // ignore... - it's a 'hash' to another page.
6152 Roo.log("NavItem - prevent Default?");
6154 this.scrollToElement(e);
6158 var p = this.parent();
6160 if (['tabs','pills'].indexOf(p.type)!==-1) {
6161 if (typeof(p.setActiveItem) !== 'undefined') {
6162 p.setActiveItem(this);
6166 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6167 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6168 // remove the collapsed menu expand...
6169 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6173 isActive: function () {
6176 setActive : function(state, fire, is_was_active)
6178 if (this.active && !state && this.navId) {
6179 this.was_active = true;
6180 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6182 nv.clearWasActive(this);
6186 this.active = state;
6189 this.el.removeClass('active');
6190 this.navLink ? this.navLink.removeClass('active') : false;
6191 } else if (!this.el.hasClass('active')) {
6193 this.el.addClass('active');
6194 if (Roo.bootstrap.version == 4 && this.navLink ) {
6195 this.navLink.addClass('active');
6200 this.fireEvent('changed', this, state);
6203 // show a panel if it's registered and related..
6205 if (!this.navId || !this.tabId || !state || is_was_active) {
6209 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6213 var pan = tg.getPanelByName(this.tabId);
6217 // if we can not flip to new panel - go back to old nav highlight..
6218 if (false == tg.showPanel(pan)) {
6219 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6221 var onav = nv.getWasActive();
6223 onav.setActive(true, false, true);
6232 // this should not be here...
6233 setDisabled : function(state)
6235 this.disabled = state;
6237 this.el.removeClass('disabled');
6238 } else if (!this.el.hasClass('disabled')) {
6239 this.el.addClass('disabled');
6245 * Fetch the element to display the tooltip on.
6246 * @return {Roo.Element} defaults to this.el
6248 tooltipEl : function()
6250 return this.el.select('' + this.tagtype + '', true).first();
6253 scrollToElement : function(e)
6255 var c = document.body;
6258 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6260 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6261 c = document.documentElement;
6264 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6270 var o = target.calcOffsetsTo(c);
6277 this.fireEvent('scrollto', this, options, e);
6279 Roo.get(c).scrollTo('top', options.value, true);
6292 * <span> icon </span>
6293 * <span> text </span>
6294 * <span>badge </span>
6298 * @class Roo.bootstrap.NavSidebarItem
6299 * @extends Roo.bootstrap.NavItem
6300 * Bootstrap Navbar.NavSidebarItem class
6301 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6302 * {Boolean} open is the menu open
6303 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6304 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6305 * {String} buttonSize (sm|md|lg)the extra classes for the button
6306 * {Boolean} showArrow show arrow next to the text (default true)
6308 * Create a new Navbar Button
6309 * @param {Object} config The config object
6311 Roo.bootstrap.NavSidebarItem = function(config){
6312 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6317 * The raw click event for the entire grid.
6318 * @param {Roo.EventObject} e
6323 * Fires when the active item active state changes
6324 * @param {Roo.bootstrap.NavSidebarItem} this
6325 * @param {boolean} state the new state
6333 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6335 badgeWeight : 'default',
6341 buttonWeight : 'default',
6347 getAutoCreate : function(){
6352 href : this.href || '#',
6358 if(this.buttonView){
6361 href : this.href || '#',
6362 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6375 cfg.cls += ' active';
6378 if (this.disabled) {
6379 cfg.cls += ' disabled';
6382 cfg.cls += ' open x-open';
6385 if (this.glyphicon || this.icon) {
6386 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6387 a.cn.push({ tag : 'i', cls : c }) ;
6390 if(!this.buttonView){
6393 html : this.html || ''
6400 if (this.badge !== '') {
6401 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6407 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6410 a.cls += ' dropdown-toggle treeview' ;
6416 initEvents : function()
6418 if (typeof (this.menu) != 'undefined') {
6419 this.menu.parentType = this.xtype;
6420 this.menu.triggerEl = this.el;
6421 this.menu = this.addxtype(Roo.apply({}, this.menu));
6424 this.el.on('click', this.onClick, this);
6426 if(this.badge !== ''){
6427 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6432 onClick : function(e)
6439 if(this.preventDefault){
6443 this.fireEvent('click', this, e);
6446 disable : function()
6448 this.setDisabled(true);
6453 this.setDisabled(false);
6456 setDisabled : function(state)
6458 if(this.disabled == state){
6462 this.disabled = state;
6465 this.el.addClass('disabled');
6469 this.el.removeClass('disabled');
6474 setActive : function(state)
6476 if(this.active == state){
6480 this.active = state;
6483 this.el.addClass('active');
6487 this.el.removeClass('active');
6492 isActive: function ()
6497 setBadge : function(str)
6503 this.badgeEl.dom.innerHTML = str;
6520 * @class Roo.bootstrap.Row
6521 * @extends Roo.bootstrap.Component
6522 * Bootstrap Row class (contains columns...)
6526 * @param {Object} config The config object
6529 Roo.bootstrap.Row = function(config){
6530 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6533 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6535 getAutoCreate : function(){
6554 * @class Roo.bootstrap.Pagination
6555 * @extends Roo.bootstrap.Component
6556 * Bootstrap Pagination class
6557 * @cfg {String} size xs | sm | md | lg
6558 * @cfg {Boolean} inverse false | true
6561 * Create a new Pagination
6562 * @param {Object} config The config object
6565 Roo.bootstrap.Pagination = function(config){
6566 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6569 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6575 getAutoCreate : function(){
6581 cfg.cls += ' inverse';
6587 cfg.cls += " " + this.cls;
6605 * @class Roo.bootstrap.PaginationItem
6606 * @extends Roo.bootstrap.Component
6607 * Bootstrap PaginationItem class
6608 * @cfg {String} html text
6609 * @cfg {String} href the link
6610 * @cfg {Boolean} preventDefault (true | false) default true
6611 * @cfg {Boolean} active (true | false) default false
6612 * @cfg {Boolean} disabled default false
6616 * Create a new PaginationItem
6617 * @param {Object} config The config object
6621 Roo.bootstrap.PaginationItem = function(config){
6622 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6627 * The raw click event for the entire grid.
6628 * @param {Roo.EventObject} e
6634 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6638 preventDefault: true,
6643 getAutoCreate : function(){
6649 href : this.href ? this.href : '#',
6650 html : this.html ? this.html : ''
6660 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6664 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6670 initEvents: function() {
6672 this.el.on('click', this.onClick, this);
6675 onClick : function(e)
6677 Roo.log('PaginationItem on click ');
6678 if(this.preventDefault){
6686 this.fireEvent('click', this, e);
6702 * @class Roo.bootstrap.Slider
6703 * @extends Roo.bootstrap.Component
6704 * Bootstrap Slider class
6707 * Create a new Slider
6708 * @param {Object} config The config object
6711 Roo.bootstrap.Slider = function(config){
6712 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6715 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6717 getAutoCreate : function(){
6721 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6725 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6737 * Ext JS Library 1.1.1
6738 * Copyright(c) 2006-2007, Ext JS, LLC.
6740 * Originally Released Under LGPL - original licence link has changed is not relivant.
6743 * <script type="text/javascript">
6748 * @class Roo.grid.ColumnModel
6749 * @extends Roo.util.Observable
6750 * This is the default implementation of a ColumnModel used by the Grid. It defines
6751 * the columns in the grid.
6754 var colModel = new Roo.grid.ColumnModel([
6755 {header: "Ticker", width: 60, sortable: true, locked: true},
6756 {header: "Company Name", width: 150, sortable: true},
6757 {header: "Market Cap.", width: 100, sortable: true},
6758 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6759 {header: "Employees", width: 100, sortable: true, resizable: false}
6764 * The config options listed for this class are options which may appear in each
6765 * individual column definition.
6766 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6768 * @param {Object} config An Array of column config objects. See this class's
6769 * config objects for details.
6771 Roo.grid.ColumnModel = function(config){
6773 * The config passed into the constructor
6775 this.config = config;
6778 // if no id, create one
6779 // if the column does not have a dataIndex mapping,
6780 // map it to the order it is in the config
6781 for(var i = 0, len = config.length; i < len; i++){
6783 if(typeof c.dataIndex == "undefined"){
6786 if(typeof c.renderer == "string"){
6787 c.renderer = Roo.util.Format[c.renderer];
6789 if(typeof c.id == "undefined"){
6792 if(c.editor && c.editor.xtype){
6793 c.editor = Roo.factory(c.editor, Roo.grid);
6795 if(c.editor && c.editor.isFormField){
6796 c.editor = new Roo.grid.GridEditor(c.editor);
6798 this.lookup[c.id] = c;
6802 * The width of columns which have no width specified (defaults to 100)
6805 this.defaultWidth = 100;
6808 * Default sortable of columns which have no sortable specified (defaults to false)
6811 this.defaultSortable = false;
6815 * @event widthchange
6816 * Fires when the width of a column changes.
6817 * @param {ColumnModel} this
6818 * @param {Number} columnIndex The column index
6819 * @param {Number} newWidth The new width
6821 "widthchange": true,
6823 * @event headerchange
6824 * Fires when the text of a header changes.
6825 * @param {ColumnModel} this
6826 * @param {Number} columnIndex The column index
6827 * @param {Number} newText The new header text
6829 "headerchange": true,
6831 * @event hiddenchange
6832 * Fires when a column is hidden or "unhidden".
6833 * @param {ColumnModel} this
6834 * @param {Number} columnIndex The column index
6835 * @param {Boolean} hidden true if hidden, false otherwise
6837 "hiddenchange": true,
6839 * @event columnmoved
6840 * Fires when a column is moved.
6841 * @param {ColumnModel} this
6842 * @param {Number} oldIndex
6843 * @param {Number} newIndex
6845 "columnmoved" : true,
6847 * @event columlockchange
6848 * Fires when a column's locked state is changed
6849 * @param {ColumnModel} this
6850 * @param {Number} colIndex
6851 * @param {Boolean} locked true if locked
6853 "columnlockchange" : true
6855 Roo.grid.ColumnModel.superclass.constructor.call(this);
6857 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6859 * @cfg {String} header The header text to display in the Grid view.
6862 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6863 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6864 * specified, the column's index is used as an index into the Record's data Array.
6867 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6868 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6871 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6872 * Defaults to the value of the {@link #defaultSortable} property.
6873 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6876 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6879 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6882 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6885 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6888 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6889 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6890 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6891 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6894 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6897 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6900 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6903 * @cfg {String} cursor (Optional)
6906 * @cfg {String} tooltip (Optional)
6909 * @cfg {Number} xs (Optional)
6912 * @cfg {Number} sm (Optional)
6915 * @cfg {Number} md (Optional)
6918 * @cfg {Number} lg (Optional)
6921 * Returns the id of the column at the specified index.
6922 * @param {Number} index The column index
6923 * @return {String} the id
6925 getColumnId : function(index){
6926 return this.config[index].id;
6930 * Returns the column for a specified id.
6931 * @param {String} id The column id
6932 * @return {Object} the column
6934 getColumnById : function(id){
6935 return this.lookup[id];
6940 * Returns the column for a specified dataIndex.
6941 * @param {String} dataIndex The column dataIndex
6942 * @return {Object|Boolean} the column or false if not found
6944 getColumnByDataIndex: function(dataIndex){
6945 var index = this.findColumnIndex(dataIndex);
6946 return index > -1 ? this.config[index] : false;
6950 * Returns the index for a specified column id.
6951 * @param {String} id The column id
6952 * @return {Number} the index, or -1 if not found
6954 getIndexById : function(id){
6955 for(var i = 0, len = this.config.length; i < len; i++){
6956 if(this.config[i].id == id){
6964 * Returns the index for a specified column dataIndex.
6965 * @param {String} dataIndex The column dataIndex
6966 * @return {Number} the index, or -1 if not found
6969 findColumnIndex : function(dataIndex){
6970 for(var i = 0, len = this.config.length; i < len; i++){
6971 if(this.config[i].dataIndex == dataIndex){
6979 moveColumn : function(oldIndex, newIndex){
6980 var c = this.config[oldIndex];
6981 this.config.splice(oldIndex, 1);
6982 this.config.splice(newIndex, 0, c);
6983 this.dataMap = null;
6984 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6987 isLocked : function(colIndex){
6988 return this.config[colIndex].locked === true;
6991 setLocked : function(colIndex, value, suppressEvent){
6992 if(this.isLocked(colIndex) == value){
6995 this.config[colIndex].locked = value;
6997 this.fireEvent("columnlockchange", this, colIndex, value);
7001 getTotalLockedWidth : function(){
7003 for(var i = 0; i < this.config.length; i++){
7004 if(this.isLocked(i) && !this.isHidden(i)){
7005 this.totalWidth += this.getColumnWidth(i);
7011 getLockedCount : function(){
7012 for(var i = 0, len = this.config.length; i < len; i++){
7013 if(!this.isLocked(i)){
7018 return this.config.length;
7022 * Returns the number of columns.
7025 getColumnCount : function(visibleOnly){
7026 if(visibleOnly === true){
7028 for(var i = 0, len = this.config.length; i < len; i++){
7029 if(!this.isHidden(i)){
7035 return this.config.length;
7039 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7040 * @param {Function} fn
7041 * @param {Object} scope (optional)
7042 * @return {Array} result
7044 getColumnsBy : function(fn, scope){
7046 for(var i = 0, len = this.config.length; i < len; i++){
7047 var c = this.config[i];
7048 if(fn.call(scope||this, c, i) === true){
7056 * Returns true if the specified column is sortable.
7057 * @param {Number} col The column index
7060 isSortable : function(col){
7061 if(typeof this.config[col].sortable == "undefined"){
7062 return this.defaultSortable;
7064 return this.config[col].sortable;
7068 * Returns the rendering (formatting) function defined for the column.
7069 * @param {Number} col The column index.
7070 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7072 getRenderer : function(col){
7073 if(!this.config[col].renderer){
7074 return Roo.grid.ColumnModel.defaultRenderer;
7076 return this.config[col].renderer;
7080 * Sets the rendering (formatting) function for a column.
7081 * @param {Number} col The column index
7082 * @param {Function} fn The function to use to process the cell's raw data
7083 * to return HTML markup for the grid view. The render function is called with
7084 * the following parameters:<ul>
7085 * <li>Data value.</li>
7086 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7087 * <li>css A CSS style string to apply to the table cell.</li>
7088 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7089 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7090 * <li>Row index</li>
7091 * <li>Column index</li>
7092 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7094 setRenderer : function(col, fn){
7095 this.config[col].renderer = fn;
7099 * Returns the width for the specified column.
7100 * @param {Number} col The column index
7103 getColumnWidth : function(col){
7104 return this.config[col].width * 1 || this.defaultWidth;
7108 * Sets the width for a column.
7109 * @param {Number} col The column index
7110 * @param {Number} width The new width
7112 setColumnWidth : function(col, width, suppressEvent){
7113 this.config[col].width = width;
7114 this.totalWidth = null;
7116 this.fireEvent("widthchange", this, col, width);
7121 * Returns the total width of all columns.
7122 * @param {Boolean} includeHidden True to include hidden column widths
7125 getTotalWidth : function(includeHidden){
7126 if(!this.totalWidth){
7127 this.totalWidth = 0;
7128 for(var i = 0, len = this.config.length; i < len; i++){
7129 if(includeHidden || !this.isHidden(i)){
7130 this.totalWidth += this.getColumnWidth(i);
7134 return this.totalWidth;
7138 * Returns the header for the specified column.
7139 * @param {Number} col The column index
7142 getColumnHeader : function(col){
7143 return this.config[col].header;
7147 * Sets the header for a column.
7148 * @param {Number} col The column index
7149 * @param {String} header The new header
7151 setColumnHeader : function(col, header){
7152 this.config[col].header = header;
7153 this.fireEvent("headerchange", this, col, header);
7157 * Returns the tooltip for the specified column.
7158 * @param {Number} col The column index
7161 getColumnTooltip : function(col){
7162 return this.config[col].tooltip;
7165 * Sets the tooltip for a column.
7166 * @param {Number} col The column index
7167 * @param {String} tooltip The new tooltip
7169 setColumnTooltip : function(col, tooltip){
7170 this.config[col].tooltip = tooltip;
7174 * Returns the dataIndex for the specified column.
7175 * @param {Number} col The column index
7178 getDataIndex : function(col){
7179 return this.config[col].dataIndex;
7183 * Sets the dataIndex for a column.
7184 * @param {Number} col The column index
7185 * @param {Number} dataIndex The new dataIndex
7187 setDataIndex : function(col, dataIndex){
7188 this.config[col].dataIndex = dataIndex;
7194 * Returns true if the cell is editable.
7195 * @param {Number} colIndex The column index
7196 * @param {Number} rowIndex The row index - this is nto actually used..?
7199 isCellEditable : function(colIndex, rowIndex){
7200 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7204 * Returns the editor defined for the cell/column.
7205 * return false or null to disable editing.
7206 * @param {Number} colIndex The column index
7207 * @param {Number} rowIndex The row index
7210 getCellEditor : function(colIndex, rowIndex){
7211 return this.config[colIndex].editor;
7215 * Sets if a column is editable.
7216 * @param {Number} col The column index
7217 * @param {Boolean} editable True if the column is editable
7219 setEditable : function(col, editable){
7220 this.config[col].editable = editable;
7225 * Returns true if the column is hidden.
7226 * @param {Number} colIndex The column index
7229 isHidden : function(colIndex){
7230 return this.config[colIndex].hidden;
7235 * Returns true if the column width cannot be changed
7237 isFixed : function(colIndex){
7238 return this.config[colIndex].fixed;
7242 * Returns true if the column can be resized
7245 isResizable : function(colIndex){
7246 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7249 * Sets if a column is hidden.
7250 * @param {Number} colIndex The column index
7251 * @param {Boolean} hidden True if the column is hidden
7253 setHidden : function(colIndex, hidden){
7254 this.config[colIndex].hidden = hidden;
7255 this.totalWidth = null;
7256 this.fireEvent("hiddenchange", this, colIndex, hidden);
7260 * Sets the editor for a column.
7261 * @param {Number} col The column index
7262 * @param {Object} editor The editor object
7264 setEditor : function(col, editor){
7265 this.config[col].editor = editor;
7269 Roo.grid.ColumnModel.defaultRenderer = function(value)
7271 if(typeof value == "object") {
7274 if(typeof value == "string" && value.length < 1){
7278 return String.format("{0}", value);
7281 // Alias for backwards compatibility
7282 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7285 * Ext JS Library 1.1.1
7286 * Copyright(c) 2006-2007, Ext JS, LLC.
7288 * Originally Released Under LGPL - original licence link has changed is not relivant.
7291 * <script type="text/javascript">
7295 * @class Roo.LoadMask
7296 * A simple utility class for generically masking elements while loading data. If the element being masked has
7297 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7298 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7299 * element's UpdateManager load indicator and will be destroyed after the initial load.
7301 * Create a new LoadMask
7302 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7303 * @param {Object} config The config object
7305 Roo.LoadMask = function(el, config){
7306 this.el = Roo.get(el);
7307 Roo.apply(this, config);
7309 this.store.on('beforeload', this.onBeforeLoad, this);
7310 this.store.on('load', this.onLoad, this);
7311 this.store.on('loadexception', this.onLoadException, this);
7312 this.removeMask = false;
7314 var um = this.el.getUpdateManager();
7315 um.showLoadIndicator = false; // disable the default indicator
7316 um.on('beforeupdate', this.onBeforeLoad, this);
7317 um.on('update', this.onLoad, this);
7318 um.on('failure', this.onLoad, this);
7319 this.removeMask = true;
7323 Roo.LoadMask.prototype = {
7325 * @cfg {Boolean} removeMask
7326 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7327 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7331 * The text to display in a centered loading message box (defaults to 'Loading...')
7335 * @cfg {String} msgCls
7336 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7338 msgCls : 'x-mask-loading',
7341 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7347 * Disables the mask to prevent it from being displayed
7349 disable : function(){
7350 this.disabled = true;
7354 * Enables the mask so that it can be displayed
7356 enable : function(){
7357 this.disabled = false;
7360 onLoadException : function()
7364 if (typeof(arguments[3]) != 'undefined') {
7365 Roo.MessageBox.alert("Error loading",arguments[3]);
7369 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7370 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7377 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7382 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7386 onBeforeLoad : function(){
7388 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7393 destroy : function(){
7395 this.store.un('beforeload', this.onBeforeLoad, this);
7396 this.store.un('load', this.onLoad, this);
7397 this.store.un('loadexception', this.onLoadException, this);
7399 var um = this.el.getUpdateManager();
7400 um.un('beforeupdate', this.onBeforeLoad, this);
7401 um.un('update', this.onLoad, this);
7402 um.un('failure', this.onLoad, this);
7413 * @class Roo.bootstrap.Table
7414 * @extends Roo.bootstrap.Component
7415 * Bootstrap Table class
7416 * @cfg {String} cls table class
7417 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7418 * @cfg {String} bgcolor Specifies the background color for a table
7419 * @cfg {Number} border Specifies whether the table cells should have borders or not
7420 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7421 * @cfg {Number} cellspacing Specifies the space between cells
7422 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7423 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7424 * @cfg {String} sortable Specifies that the table should be sortable
7425 * @cfg {String} summary Specifies a summary of the content of a table
7426 * @cfg {Number} width Specifies the width of a table
7427 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7429 * @cfg {boolean} striped Should the rows be alternative striped
7430 * @cfg {boolean} bordered Add borders to the table
7431 * @cfg {boolean} hover Add hover highlighting
7432 * @cfg {boolean} condensed Format condensed
7433 * @cfg {boolean} responsive Format condensed
7434 * @cfg {Boolean} loadMask (true|false) default false
7435 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7436 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7437 * @cfg {Boolean} rowSelection (true|false) default false
7438 * @cfg {Boolean} cellSelection (true|false) default false
7439 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7440 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7441 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7442 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7446 * Create a new Table
7447 * @param {Object} config The config object
7450 Roo.bootstrap.Table = function(config){
7451 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7456 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7457 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7458 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7459 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7461 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7463 this.sm.grid = this;
7464 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7465 this.sm = this.selModel;
7466 this.sm.xmodule = this.xmodule || false;
7469 if (this.cm && typeof(this.cm.config) == 'undefined') {
7470 this.colModel = new Roo.grid.ColumnModel(this.cm);
7471 this.cm = this.colModel;
7472 this.cm.xmodule = this.xmodule || false;
7475 this.store= Roo.factory(this.store, Roo.data);
7476 this.ds = this.store;
7477 this.ds.xmodule = this.xmodule || false;
7480 if (this.footer && this.store) {
7481 this.footer.dataSource = this.ds;
7482 this.footer = Roo.factory(this.footer);
7489 * Fires when a cell is clicked
7490 * @param {Roo.bootstrap.Table} this
7491 * @param {Roo.Element} el
7492 * @param {Number} rowIndex
7493 * @param {Number} columnIndex
7494 * @param {Roo.EventObject} e
7498 * @event celldblclick
7499 * Fires when a cell is double clicked
7500 * @param {Roo.bootstrap.Table} this
7501 * @param {Roo.Element} el
7502 * @param {Number} rowIndex
7503 * @param {Number} columnIndex
7504 * @param {Roo.EventObject} e
7506 "celldblclick" : true,
7509 * Fires when a row is clicked
7510 * @param {Roo.bootstrap.Table} this
7511 * @param {Roo.Element} el
7512 * @param {Number} rowIndex
7513 * @param {Roo.EventObject} e
7517 * @event rowdblclick
7518 * Fires when a row is double clicked
7519 * @param {Roo.bootstrap.Table} this
7520 * @param {Roo.Element} el
7521 * @param {Number} rowIndex
7522 * @param {Roo.EventObject} e
7524 "rowdblclick" : true,
7527 * Fires when a mouseover occur
7528 * @param {Roo.bootstrap.Table} this
7529 * @param {Roo.Element} el
7530 * @param {Number} rowIndex
7531 * @param {Number} columnIndex
7532 * @param {Roo.EventObject} e
7537 * Fires when a mouseout occur
7538 * @param {Roo.bootstrap.Table} this
7539 * @param {Roo.Element} el
7540 * @param {Number} rowIndex
7541 * @param {Number} columnIndex
7542 * @param {Roo.EventObject} e
7547 * Fires when a row is rendered, so you can change add a style to it.
7548 * @param {Roo.bootstrap.Table} this
7549 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7553 * @event rowsrendered
7554 * Fires when all the rows have been rendered
7555 * @param {Roo.bootstrap.Table} this
7557 'rowsrendered' : true,
7559 * @event contextmenu
7560 * The raw contextmenu event for the entire grid.
7561 * @param {Roo.EventObject} e
7563 "contextmenu" : true,
7565 * @event rowcontextmenu
7566 * Fires when a row is right clicked
7567 * @param {Roo.bootstrap.Table} this
7568 * @param {Number} rowIndex
7569 * @param {Roo.EventObject} e
7571 "rowcontextmenu" : true,
7573 * @event cellcontextmenu
7574 * Fires when a cell is right clicked
7575 * @param {Roo.bootstrap.Table} this
7576 * @param {Number} rowIndex
7577 * @param {Number} cellIndex
7578 * @param {Roo.EventObject} e
7580 "cellcontextmenu" : true,
7582 * @event headercontextmenu
7583 * Fires when a header is right clicked
7584 * @param {Roo.bootstrap.Table} this
7585 * @param {Number} columnIndex
7586 * @param {Roo.EventObject} e
7588 "headercontextmenu" : true
7592 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7618 rowSelection : false,
7619 cellSelection : false,
7622 // Roo.Element - the tbody
7624 // Roo.Element - thead element
7627 container: false, // used by gridpanel...
7633 auto_hide_footer : false,
7635 getAutoCreate : function()
7637 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7644 if (this.scrollBody) {
7645 cfg.cls += ' table-body-fixed';
7648 cfg.cls += ' table-striped';
7652 cfg.cls += ' table-hover';
7654 if (this.bordered) {
7655 cfg.cls += ' table-bordered';
7657 if (this.condensed) {
7658 cfg.cls += ' table-condensed';
7660 if (this.responsive) {
7661 cfg.cls += ' table-responsive';
7665 cfg.cls+= ' ' +this.cls;
7668 // this lot should be simplifed...
7681 ].forEach(function(k) {
7689 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7692 if(this.store || this.cm){
7693 if(this.headerShow){
7694 cfg.cn.push(this.renderHeader());
7697 cfg.cn.push(this.renderBody());
7699 if(this.footerShow){
7700 cfg.cn.push(this.renderFooter());
7702 // where does this come from?
7703 //cfg.cls+= ' TableGrid';
7706 return { cn : [ cfg ] };
7709 initEvents : function()
7711 if(!this.store || !this.cm){
7714 if (this.selModel) {
7715 this.selModel.initEvents();
7719 //Roo.log('initEvents with ds!!!!');
7721 this.mainBody = this.el.select('tbody', true).first();
7722 this.mainHead = this.el.select('thead', true).first();
7723 this.mainFoot = this.el.select('tfoot', true).first();
7729 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7730 e.on('click', _this.sort, _this);
7733 this.mainBody.on("click", this.onClick, this);
7734 this.mainBody.on("dblclick", this.onDblClick, this);
7736 // why is this done????? = it breaks dialogs??
7737 //this.parent().el.setStyle('position', 'relative');
7741 this.footer.parentId = this.id;
7742 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7745 this.el.select('tfoot tr td').first().addClass('hide');
7750 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7753 this.store.on('load', this.onLoad, this);
7754 this.store.on('beforeload', this.onBeforeLoad, this);
7755 this.store.on('update', this.onUpdate, this);
7756 this.store.on('add', this.onAdd, this);
7757 this.store.on("clear", this.clear, this);
7759 this.el.on("contextmenu", this.onContextMenu, this);
7761 this.mainBody.on('scroll', this.onBodyScroll, this);
7763 this.cm.on("headerchange", this.onHeaderChange, this);
7765 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7769 onContextMenu : function(e, t)
7771 this.processEvent("contextmenu", e);
7774 processEvent : function(name, e)
7776 if (name != 'touchstart' ) {
7777 this.fireEvent(name, e);
7780 var t = e.getTarget();
7782 var cell = Roo.get(t);
7788 if(cell.findParent('tfoot', false, true)){
7792 if(cell.findParent('thead', false, true)){
7794 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7795 cell = Roo.get(t).findParent('th', false, true);
7797 Roo.log("failed to find th in thead?");
7798 Roo.log(e.getTarget());
7803 var cellIndex = cell.dom.cellIndex;
7805 var ename = name == 'touchstart' ? 'click' : name;
7806 this.fireEvent("header" + ename, this, cellIndex, e);
7811 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7812 cell = Roo.get(t).findParent('td', false, true);
7814 Roo.log("failed to find th in tbody?");
7815 Roo.log(e.getTarget());
7820 var row = cell.findParent('tr', false, true);
7821 var cellIndex = cell.dom.cellIndex;
7822 var rowIndex = row.dom.rowIndex - 1;
7826 this.fireEvent("row" + name, this, rowIndex, e);
7830 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7836 onMouseover : function(e, el)
7838 var cell = Roo.get(el);
7844 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7845 cell = cell.findParent('td', false, true);
7848 var row = cell.findParent('tr', false, true);
7849 var cellIndex = cell.dom.cellIndex;
7850 var rowIndex = row.dom.rowIndex - 1; // start from 0
7852 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7856 onMouseout : function(e, el)
7858 var cell = Roo.get(el);
7864 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7865 cell = cell.findParent('td', false, true);
7868 var row = cell.findParent('tr', false, true);
7869 var cellIndex = cell.dom.cellIndex;
7870 var rowIndex = row.dom.rowIndex - 1; // start from 0
7872 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7876 onClick : function(e, el)
7878 var cell = Roo.get(el);
7880 if(!cell || (!this.cellSelection && !this.rowSelection)){
7884 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7885 cell = cell.findParent('td', false, true);
7888 if(!cell || typeof(cell) == 'undefined'){
7892 var row = cell.findParent('tr', false, true);
7894 if(!row || typeof(row) == 'undefined'){
7898 var cellIndex = cell.dom.cellIndex;
7899 var rowIndex = this.getRowIndex(row);
7901 // why??? - should these not be based on SelectionModel?
7902 if(this.cellSelection){
7903 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7906 if(this.rowSelection){
7907 this.fireEvent('rowclick', this, row, rowIndex, e);
7913 onDblClick : function(e,el)
7915 var cell = Roo.get(el);
7917 if(!cell || (!this.cellSelection && !this.rowSelection)){
7921 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7922 cell = cell.findParent('td', false, true);
7925 if(!cell || typeof(cell) == 'undefined'){
7929 var row = cell.findParent('tr', false, true);
7931 if(!row || typeof(row) == 'undefined'){
7935 var cellIndex = cell.dom.cellIndex;
7936 var rowIndex = this.getRowIndex(row);
7938 if(this.cellSelection){
7939 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7942 if(this.rowSelection){
7943 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7947 sort : function(e,el)
7949 var col = Roo.get(el);
7951 if(!col.hasClass('sortable')){
7955 var sort = col.attr('sort');
7958 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7962 this.store.sortInfo = {field : sort, direction : dir};
7965 Roo.log("calling footer first");
7966 this.footer.onClick('first');
7969 this.store.load({ params : { start : 0 } });
7973 renderHeader : function()
7981 this.totalWidth = 0;
7983 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7985 var config = cm.config[i];
7989 cls : 'x-hcol-' + i,
7991 html: cm.getColumnHeader(i)
7996 if(typeof(config.sortable) != 'undefined' && config.sortable){
7998 c.html = '<i class="glyphicon"></i>' + c.html;
8001 // could use BS4 hidden-..-down
8003 if(typeof(config.lgHeader) != 'undefined'){
8004 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8007 if(typeof(config.mdHeader) != 'undefined'){
8008 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8011 if(typeof(config.smHeader) != 'undefined'){
8012 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8015 if(typeof(config.xsHeader) != 'undefined'){
8016 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8023 if(typeof(config.tooltip) != 'undefined'){
8024 c.tooltip = config.tooltip;
8027 if(typeof(config.colspan) != 'undefined'){
8028 c.colspan = config.colspan;
8031 if(typeof(config.hidden) != 'undefined' && config.hidden){
8032 c.style += ' display:none;';
8035 if(typeof(config.dataIndex) != 'undefined'){
8036 c.sort = config.dataIndex;
8041 if(typeof(config.align) != 'undefined' && config.align.length){
8042 c.style += ' text-align:' + config.align + ';';
8045 if(typeof(config.width) != 'undefined'){
8046 c.style += ' width:' + config.width + 'px;';
8047 this.totalWidth += config.width;
8049 this.totalWidth += 100; // assume minimum of 100 per column?
8052 if(typeof(config.cls) != 'undefined'){
8053 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8056 ['xs','sm','md','lg'].map(function(size){
8058 if(typeof(config[size]) == 'undefined'){
8062 if (!config[size]) { // 0 = hidden
8063 // BS 4 '0' is treated as hide that column and below.
8064 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8068 c.cls += ' col-' + size + '-' + config[size] + (
8069 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8081 renderBody : function()
8091 colspan : this.cm.getColumnCount()
8101 renderFooter : function()
8111 colspan : this.cm.getColumnCount()
8125 // Roo.log('ds onload');
8130 var ds = this.store;
8132 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8133 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8134 if (_this.store.sortInfo) {
8136 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8137 e.select('i', true).addClass(['glyphicon-arrow-up']);
8140 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8141 e.select('i', true).addClass(['glyphicon-arrow-down']);
8146 var tbody = this.mainBody;
8148 if(ds.getCount() > 0){
8149 ds.data.each(function(d,rowIndex){
8150 var row = this.renderRow(cm, ds, rowIndex);
8152 tbody.createChild(row);
8156 if(row.cellObjects.length){
8157 Roo.each(row.cellObjects, function(r){
8158 _this.renderCellObject(r);
8165 var tfoot = this.el.select('tfoot', true).first();
8167 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8169 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8171 var total = this.ds.getTotalCount();
8173 if(this.footer.pageSize < total){
8174 this.mainFoot.show();
8178 Roo.each(this.el.select('tbody td', true).elements, function(e){
8179 e.on('mouseover', _this.onMouseover, _this);
8182 Roo.each(this.el.select('tbody td', true).elements, function(e){
8183 e.on('mouseout', _this.onMouseout, _this);
8185 this.fireEvent('rowsrendered', this);
8191 onUpdate : function(ds,record)
8193 this.refreshRow(record);
8197 onRemove : function(ds, record, index, isUpdate){
8198 if(isUpdate !== true){
8199 this.fireEvent("beforerowremoved", this, index, record);
8201 var bt = this.mainBody.dom;
8203 var rows = this.el.select('tbody > tr', true).elements;
8205 if(typeof(rows[index]) != 'undefined'){
8206 bt.removeChild(rows[index].dom);
8209 // if(bt.rows[index]){
8210 // bt.removeChild(bt.rows[index]);
8213 if(isUpdate !== true){
8214 //this.stripeRows(index);
8215 //this.syncRowHeights(index, index);
8217 this.fireEvent("rowremoved", this, index, record);
8221 onAdd : function(ds, records, rowIndex)
8223 //Roo.log('on Add called');
8224 // - note this does not handle multiple adding very well..
8225 var bt = this.mainBody.dom;
8226 for (var i =0 ; i < records.length;i++) {
8227 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8228 //Roo.log(records[i]);
8229 //Roo.log(this.store.getAt(rowIndex+i));
8230 this.insertRow(this.store, rowIndex + i, false);
8237 refreshRow : function(record){
8238 var ds = this.store, index;
8239 if(typeof record == 'number'){
8241 record = ds.getAt(index);
8243 index = ds.indexOf(record);
8245 this.insertRow(ds, index, true);
8247 this.onRemove(ds, record, index+1, true);
8249 //this.syncRowHeights(index, index);
8251 this.fireEvent("rowupdated", this, index, record);
8254 insertRow : function(dm, rowIndex, isUpdate){
8257 this.fireEvent("beforerowsinserted", this, rowIndex);
8259 //var s = this.getScrollState();
8260 var row = this.renderRow(this.cm, this.store, rowIndex);
8261 // insert before rowIndex..
8262 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8266 if(row.cellObjects.length){
8267 Roo.each(row.cellObjects, function(r){
8268 _this.renderCellObject(r);
8273 this.fireEvent("rowsinserted", this, rowIndex);
8274 //this.syncRowHeights(firstRow, lastRow);
8275 //this.stripeRows(firstRow);
8282 getRowDom : function(rowIndex)
8284 var rows = this.el.select('tbody > tr', true).elements;
8286 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8289 // returns the object tree for a tr..
8292 renderRow : function(cm, ds, rowIndex)
8294 var d = ds.getAt(rowIndex);
8298 cls : 'x-row-' + rowIndex,
8302 var cellObjects = [];
8304 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8305 var config = cm.config[i];
8307 var renderer = cm.getRenderer(i);
8311 if(typeof(renderer) !== 'undefined'){
8312 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8314 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8315 // and are rendered into the cells after the row is rendered - using the id for the element.
8317 if(typeof(value) === 'object'){
8327 rowIndex : rowIndex,
8332 this.fireEvent('rowclass', this, rowcfg);
8336 cls : rowcfg.rowClass + ' x-col-' + i,
8338 html: (typeof(value) === 'object') ? '' : value
8345 if(typeof(config.colspan) != 'undefined'){
8346 td.colspan = config.colspan;
8349 if(typeof(config.hidden) != 'undefined' && config.hidden){
8350 td.style += ' display:none;';
8353 if(typeof(config.align) != 'undefined' && config.align.length){
8354 td.style += ' text-align:' + config.align + ';';
8356 if(typeof(config.valign) != 'undefined' && config.valign.length){
8357 td.style += ' vertical-align:' + config.valign + ';';
8360 if(typeof(config.width) != 'undefined'){
8361 td.style += ' width:' + config.width + 'px;';
8364 if(typeof(config.cursor) != 'undefined'){
8365 td.style += ' cursor:' + config.cursor + ';';
8368 if(typeof(config.cls) != 'undefined'){
8369 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8372 ['xs','sm','md','lg'].map(function(size){
8374 if(typeof(config[size]) == 'undefined'){
8380 if (!config[size]) { // 0 = hidden
8381 // BS 4 '0' is treated as hide that column and below.
8382 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8386 td.cls += ' col-' + size + '-' + config[size] + (
8387 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8397 row.cellObjects = cellObjects;
8405 onBeforeLoad : function()
8414 this.el.select('tbody', true).first().dom.innerHTML = '';
8417 * Show or hide a row.
8418 * @param {Number} rowIndex to show or hide
8419 * @param {Boolean} state hide
8421 setRowVisibility : function(rowIndex, state)
8423 var bt = this.mainBody.dom;
8425 var rows = this.el.select('tbody > tr', true).elements;
8427 if(typeof(rows[rowIndex]) == 'undefined'){
8430 rows[rowIndex].dom.style.display = state ? '' : 'none';
8434 getSelectionModel : function(){
8436 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8438 return this.selModel;
8441 * Render the Roo.bootstrap object from renderder
8443 renderCellObject : function(r)
8447 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8449 var t = r.cfg.render(r.container);
8452 Roo.each(r.cfg.cn, function(c){
8454 container: t.getChildContainer(),
8457 _this.renderCellObject(child);
8462 getRowIndex : function(row)
8466 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8477 * Returns the grid's underlying element = used by panel.Grid
8478 * @return {Element} The element
8480 getGridEl : function(){
8484 * Forces a resize - used by panel.Grid
8485 * @return {Element} The element
8487 autoSize : function()
8489 //var ctr = Roo.get(this.container.dom.parentElement);
8490 var ctr = Roo.get(this.el.dom);
8492 var thd = this.getGridEl().select('thead',true).first();
8493 var tbd = this.getGridEl().select('tbody', true).first();
8494 var tfd = this.getGridEl().select('tfoot', true).first();
8496 var cw = ctr.getWidth();
8500 tbd.setWidth(ctr.getWidth());
8501 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8502 // this needs fixing for various usage - currently only hydra job advers I think..
8504 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8506 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8509 cw = Math.max(cw, this.totalWidth);
8510 this.getGridEl().select('tr',true).setWidth(cw);
8511 // resize 'expandable coloumn?
8513 return; // we doe not have a view in this design..
8516 onBodyScroll: function()
8518 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8520 this.mainHead.setStyle({
8521 'position' : 'relative',
8522 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8528 var scrollHeight = this.mainBody.dom.scrollHeight;
8530 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8532 var height = this.mainBody.getHeight();
8534 if(scrollHeight - height == scrollTop) {
8536 var total = this.ds.getTotalCount();
8538 if(this.footer.cursor + this.footer.pageSize < total){
8540 this.footer.ds.load({
8542 start : this.footer.cursor + this.footer.pageSize,
8543 limit : this.footer.pageSize
8553 onHeaderChange : function()
8555 var header = this.renderHeader();
8556 var table = this.el.select('table', true).first();
8558 this.mainHead.remove();
8559 this.mainHead = table.createChild(header, this.mainBody, false);
8562 onHiddenChange : function(colModel, colIndex, hidden)
8564 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8565 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8567 this.CSS.updateRule(thSelector, "display", "");
8568 this.CSS.updateRule(tdSelector, "display", "");
8571 this.CSS.updateRule(thSelector, "display", "none");
8572 this.CSS.updateRule(tdSelector, "display", "none");
8575 this.onHeaderChange();
8579 setColumnWidth: function(col_index, width)
8581 // width = "md-2 xs-2..."
8582 if(!this.colModel.config[col_index]) {
8586 var w = width.split(" ");
8588 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8590 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8593 for(var j = 0; j < w.length; j++) {
8599 var size_cls = w[j].split("-");
8601 if(!Number.isInteger(size_cls[1] * 1)) {
8605 if(!this.colModel.config[col_index][size_cls[0]]) {
8609 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8613 h_row[0].classList.replace(
8614 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8615 "col-"+size_cls[0]+"-"+size_cls[1]
8618 for(var i = 0; i < rows.length; i++) {
8620 var size_cls = w[j].split("-");
8622 if(!Number.isInteger(size_cls[1] * 1)) {
8626 if(!this.colModel.config[col_index][size_cls[0]]) {
8630 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8634 rows[i].classList.replace(
8635 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8636 "col-"+size_cls[0]+"-"+size_cls[1]
8640 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8655 * @class Roo.bootstrap.TableCell
8656 * @extends Roo.bootstrap.Component
8657 * Bootstrap TableCell class
8658 * @cfg {String} html cell contain text
8659 * @cfg {String} cls cell class
8660 * @cfg {String} tag cell tag (td|th) default td
8661 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8662 * @cfg {String} align Aligns the content in a cell
8663 * @cfg {String} axis Categorizes cells
8664 * @cfg {String} bgcolor Specifies the background color of a cell
8665 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8666 * @cfg {Number} colspan Specifies the number of columns a cell should span
8667 * @cfg {String} headers Specifies one or more header cells a cell is related to
8668 * @cfg {Number} height Sets the height of a cell
8669 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8670 * @cfg {Number} rowspan Sets the number of rows a cell should span
8671 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8672 * @cfg {String} valign Vertical aligns the content in a cell
8673 * @cfg {Number} width Specifies the width of a cell
8676 * Create a new TableCell
8677 * @param {Object} config The config object
8680 Roo.bootstrap.TableCell = function(config){
8681 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8684 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8704 getAutoCreate : function(){
8705 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8725 cfg.align=this.align
8731 cfg.bgcolor=this.bgcolor
8734 cfg.charoff=this.charoff
8737 cfg.colspan=this.colspan
8740 cfg.headers=this.headers
8743 cfg.height=this.height
8746 cfg.nowrap=this.nowrap
8749 cfg.rowspan=this.rowspan
8752 cfg.scope=this.scope
8755 cfg.valign=this.valign
8758 cfg.width=this.width
8777 * @class Roo.bootstrap.TableRow
8778 * @extends Roo.bootstrap.Component
8779 * Bootstrap TableRow class
8780 * @cfg {String} cls row class
8781 * @cfg {String} align Aligns the content in a table row
8782 * @cfg {String} bgcolor Specifies a background color for a table row
8783 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8784 * @cfg {String} valign Vertical aligns the content in a table row
8787 * Create a new TableRow
8788 * @param {Object} config The config object
8791 Roo.bootstrap.TableRow = function(config){
8792 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8795 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8803 getAutoCreate : function(){
8804 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8814 cfg.align = this.align;
8817 cfg.bgcolor = this.bgcolor;
8820 cfg.charoff = this.charoff;
8823 cfg.valign = this.valign;
8841 * @class Roo.bootstrap.TableBody
8842 * @extends Roo.bootstrap.Component
8843 * Bootstrap TableBody class
8844 * @cfg {String} cls element class
8845 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8846 * @cfg {String} align Aligns the content inside the element
8847 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8848 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8851 * Create a new TableBody
8852 * @param {Object} config The config object
8855 Roo.bootstrap.TableBody = function(config){
8856 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8859 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8867 getAutoCreate : function(){
8868 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8882 cfg.align = this.align;
8885 cfg.charoff = this.charoff;
8888 cfg.valign = this.valign;
8895 // initEvents : function()
8902 // this.store = Roo.factory(this.store, Roo.data);
8903 // this.store.on('load', this.onLoad, this);
8905 // this.store.load();
8909 // onLoad: function ()
8911 // this.fireEvent('load', this);
8921 * Ext JS Library 1.1.1
8922 * Copyright(c) 2006-2007, Ext JS, LLC.
8924 * Originally Released Under LGPL - original licence link has changed is not relivant.
8927 * <script type="text/javascript">
8930 // as we use this in bootstrap.
8931 Roo.namespace('Roo.form');
8933 * @class Roo.form.Action
8934 * Internal Class used to handle form actions
8936 * @param {Roo.form.BasicForm} el The form element or its id
8937 * @param {Object} config Configuration options
8942 // define the action interface
8943 Roo.form.Action = function(form, options){
8945 this.options = options || {};
8948 * Client Validation Failed
8951 Roo.form.Action.CLIENT_INVALID = 'client';
8953 * Server Validation Failed
8956 Roo.form.Action.SERVER_INVALID = 'server';
8958 * Connect to Server Failed
8961 Roo.form.Action.CONNECT_FAILURE = 'connect';
8963 * Reading Data from Server Failed
8966 Roo.form.Action.LOAD_FAILURE = 'load';
8968 Roo.form.Action.prototype = {
8970 failureType : undefined,
8971 response : undefined,
8975 run : function(options){
8980 success : function(response){
8985 handleResponse : function(response){
8989 // default connection failure
8990 failure : function(response){
8992 this.response = response;
8993 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8994 this.form.afterAction(this, false);
8997 processResponse : function(response){
8998 this.response = response;
8999 if(!response.responseText){
9002 this.result = this.handleResponse(response);
9006 // utility functions used internally
9007 getUrl : function(appendParams){
9008 var url = this.options.url || this.form.url || this.form.el.dom.action;
9010 var p = this.getParams();
9012 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9018 getMethod : function(){
9019 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9022 getParams : function(){
9023 var bp = this.form.baseParams;
9024 var p = this.options.params;
9026 if(typeof p == "object"){
9027 p = Roo.urlEncode(Roo.applyIf(p, bp));
9028 }else if(typeof p == 'string' && bp){
9029 p += '&' + Roo.urlEncode(bp);
9032 p = Roo.urlEncode(bp);
9037 createCallback : function(){
9039 success: this.success,
9040 failure: this.failure,
9042 timeout: (this.form.timeout*1000),
9043 upload: this.form.fileUpload ? this.success : undefined
9048 Roo.form.Action.Submit = function(form, options){
9049 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9052 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9055 haveProgress : false,
9056 uploadComplete : false,
9058 // uploadProgress indicator.
9059 uploadProgress : function()
9061 if (!this.form.progressUrl) {
9065 if (!this.haveProgress) {
9066 Roo.MessageBox.progress("Uploading", "Uploading");
9068 if (this.uploadComplete) {
9069 Roo.MessageBox.hide();
9073 this.haveProgress = true;
9075 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9077 var c = new Roo.data.Connection();
9079 url : this.form.progressUrl,
9084 success : function(req){
9085 //console.log(data);
9089 rdata = Roo.decode(req.responseText)
9091 Roo.log("Invalid data from server..");
9095 if (!rdata || !rdata.success) {
9097 Roo.MessageBox.alert(Roo.encode(rdata));
9100 var data = rdata.data;
9102 if (this.uploadComplete) {
9103 Roo.MessageBox.hide();
9108 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9109 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9112 this.uploadProgress.defer(2000,this);
9115 failure: function(data) {
9116 Roo.log('progress url failed ');
9127 // run get Values on the form, so it syncs any secondary forms.
9128 this.form.getValues();
9130 var o = this.options;
9131 var method = this.getMethod();
9132 var isPost = method == 'POST';
9133 if(o.clientValidation === false || this.form.isValid()){
9135 if (this.form.progressUrl) {
9136 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9137 (new Date() * 1) + '' + Math.random());
9142 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9143 form:this.form.el.dom,
9144 url:this.getUrl(!isPost),
9146 params:isPost ? this.getParams() : null,
9147 isUpload: this.form.fileUpload,
9148 formData : this.form.formData
9151 this.uploadProgress();
9153 }else if (o.clientValidation !== false){ // client validation failed
9154 this.failureType = Roo.form.Action.CLIENT_INVALID;
9155 this.form.afterAction(this, false);
9159 success : function(response)
9161 this.uploadComplete= true;
9162 if (this.haveProgress) {
9163 Roo.MessageBox.hide();
9167 var result = this.processResponse(response);
9168 if(result === true || result.success){
9169 this.form.afterAction(this, true);
9173 this.form.markInvalid(result.errors);
9174 this.failureType = Roo.form.Action.SERVER_INVALID;
9176 this.form.afterAction(this, false);
9178 failure : function(response)
9180 this.uploadComplete= true;
9181 if (this.haveProgress) {
9182 Roo.MessageBox.hide();
9185 this.response = response;
9186 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9187 this.form.afterAction(this, false);
9190 handleResponse : function(response){
9191 if(this.form.errorReader){
9192 var rs = this.form.errorReader.read(response);
9195 for(var i = 0, len = rs.records.length; i < len; i++) {
9196 var r = rs.records[i];
9200 if(errors.length < 1){
9204 success : rs.success,
9210 ret = Roo.decode(response.responseText);
9214 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9224 Roo.form.Action.Load = function(form, options){
9225 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9226 this.reader = this.form.reader;
9229 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9234 Roo.Ajax.request(Roo.apply(
9235 this.createCallback(), {
9236 method:this.getMethod(),
9237 url:this.getUrl(false),
9238 params:this.getParams()
9242 success : function(response){
9244 var result = this.processResponse(response);
9245 if(result === true || !result.success || !result.data){
9246 this.failureType = Roo.form.Action.LOAD_FAILURE;
9247 this.form.afterAction(this, false);
9250 this.form.clearInvalid();
9251 this.form.setValues(result.data);
9252 this.form.afterAction(this, true);
9255 handleResponse : function(response){
9256 if(this.form.reader){
9257 var rs = this.form.reader.read(response);
9258 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9260 success : rs.success,
9264 return Roo.decode(response.responseText);
9268 Roo.form.Action.ACTION_TYPES = {
9269 'load' : Roo.form.Action.Load,
9270 'submit' : Roo.form.Action.Submit
9279 * @class Roo.bootstrap.Form
9280 * @extends Roo.bootstrap.Component
9281 * Bootstrap Form class
9282 * @cfg {String} method GET | POST (default POST)
9283 * @cfg {String} labelAlign top | left (default top)
9284 * @cfg {String} align left | right - for navbars
9285 * @cfg {Boolean} loadMask load mask when submit (default true)
9290 * @param {Object} config The config object
9294 Roo.bootstrap.Form = function(config){
9296 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9298 Roo.bootstrap.Form.popover.apply();
9302 * @event clientvalidation
9303 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9304 * @param {Form} this
9305 * @param {Boolean} valid true if the form has passed client-side validation
9307 clientvalidation: true,
9309 * @event beforeaction
9310 * Fires before any action is performed. Return false to cancel the action.
9311 * @param {Form} this
9312 * @param {Action} action The action to be performed
9316 * @event actionfailed
9317 * Fires when an action fails.
9318 * @param {Form} this
9319 * @param {Action} action The action that failed
9321 actionfailed : true,
9323 * @event actioncomplete
9324 * Fires when an action is completed.
9325 * @param {Form} this
9326 * @param {Action} action The action that completed
9328 actioncomplete : true
9332 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9335 * @cfg {String} method
9336 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9341 * The URL to use for form actions if one isn't supplied in the action options.
9344 * @cfg {Boolean} fileUpload
9345 * Set to true if this form is a file upload.
9349 * @cfg {Object} baseParams
9350 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9354 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9358 * @cfg {Sting} align (left|right) for navbar forms
9363 activeAction : null,
9366 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9367 * element by passing it or its id or mask the form itself by passing in true.
9370 waitMsgTarget : false,
9375 * @cfg {Boolean} errorMask (true|false) default false
9380 * @cfg {Number} maskOffset Default 100
9385 * @cfg {Boolean} maskBody
9389 getAutoCreate : function(){
9393 method : this.method || 'POST',
9394 id : this.id || Roo.id(),
9397 if (this.parent().xtype.match(/^Nav/)) {
9398 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9402 if (this.labelAlign == 'left' ) {
9403 cfg.cls += ' form-horizontal';
9409 initEvents : function()
9411 this.el.on('submit', this.onSubmit, this);
9412 // this was added as random key presses on the form where triggering form submit.
9413 this.el.on('keypress', function(e) {
9414 if (e.getCharCode() != 13) {
9417 // we might need to allow it for textareas.. and some other items.
9418 // check e.getTarget().
9420 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9424 Roo.log("keypress blocked");
9432 onSubmit : function(e){
9437 * Returns true if client-side validation on the form is successful.
9440 isValid : function(){
9441 var items = this.getItems();
9445 items.each(function(f){
9451 Roo.log('invalid field: ' + f.name);
9455 if(!target && f.el.isVisible(true)){
9461 if(this.errorMask && !valid){
9462 Roo.bootstrap.Form.popover.mask(this, target);
9469 * Returns true if any fields in this form have changed since their original load.
9472 isDirty : function(){
9474 var items = this.getItems();
9475 items.each(function(f){
9485 * Performs a predefined action (submit or load) or custom actions you define on this form.
9486 * @param {String} actionName The name of the action type
9487 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9488 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9489 * accept other config options):
9491 Property Type Description
9492 ---------------- --------------- ----------------------------------------------------------------------------------
9493 url String The url for the action (defaults to the form's url)
9494 method String The form method to use (defaults to the form's method, or POST if not defined)
9495 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9496 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9497 validate the form on the client (defaults to false)
9499 * @return {BasicForm} this
9501 doAction : function(action, options){
9502 if(typeof action == 'string'){
9503 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9505 if(this.fireEvent('beforeaction', this, action) !== false){
9506 this.beforeAction(action);
9507 action.run.defer(100, action);
9513 beforeAction : function(action){
9514 var o = action.options;
9519 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9521 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9524 // not really supported yet.. ??
9526 //if(this.waitMsgTarget === true){
9527 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9528 //}else if(this.waitMsgTarget){
9529 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9530 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9532 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9538 afterAction : function(action, success){
9539 this.activeAction = null;
9540 var o = action.options;
9545 Roo.get(document.body).unmask();
9551 //if(this.waitMsgTarget === true){
9552 // this.el.unmask();
9553 //}else if(this.waitMsgTarget){
9554 // this.waitMsgTarget.unmask();
9556 // Roo.MessageBox.updateProgress(1);
9557 // Roo.MessageBox.hide();
9564 Roo.callback(o.success, o.scope, [this, action]);
9565 this.fireEvent('actioncomplete', this, action);
9569 // failure condition..
9570 // we have a scenario where updates need confirming.
9571 // eg. if a locking scenario exists..
9572 // we look for { errors : { needs_confirm : true }} in the response.
9574 (typeof(action.result) != 'undefined') &&
9575 (typeof(action.result.errors) != 'undefined') &&
9576 (typeof(action.result.errors.needs_confirm) != 'undefined')
9579 Roo.log("not supported yet");
9582 Roo.MessageBox.confirm(
9583 "Change requires confirmation",
9584 action.result.errorMsg,
9589 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9599 Roo.callback(o.failure, o.scope, [this, action]);
9600 // show an error message if no failed handler is set..
9601 if (!this.hasListener('actionfailed')) {
9602 Roo.log("need to add dialog support");
9604 Roo.MessageBox.alert("Error",
9605 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9606 action.result.errorMsg :
9607 "Saving Failed, please check your entries or try again"
9612 this.fireEvent('actionfailed', this, action);
9617 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9618 * @param {String} id The value to search for
9621 findField : function(id){
9622 var items = this.getItems();
9623 var field = items.get(id);
9625 items.each(function(f){
9626 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9633 return field || null;
9636 * Mark fields in this form invalid in bulk.
9637 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9638 * @return {BasicForm} this
9640 markInvalid : function(errors){
9641 if(errors instanceof Array){
9642 for(var i = 0, len = errors.length; i < len; i++){
9643 var fieldError = errors[i];
9644 var f = this.findField(fieldError.id);
9646 f.markInvalid(fieldError.msg);
9652 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9653 field.markInvalid(errors[id]);
9657 //Roo.each(this.childForms || [], function (f) {
9658 // f.markInvalid(errors);
9665 * Set values for fields in this form in bulk.
9666 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9667 * @return {BasicForm} this
9669 setValues : function(values){
9670 if(values instanceof Array){ // array of objects
9671 for(var i = 0, len = values.length; i < len; i++){
9673 var f = this.findField(v.id);
9675 f.setValue(v.value);
9676 if(this.trackResetOnLoad){
9677 f.originalValue = f.getValue();
9681 }else{ // object hash
9684 if(typeof values[id] != 'function' && (field = this.findField(id))){
9686 if (field.setFromData &&
9688 field.displayField &&
9689 // combos' with local stores can
9690 // be queried via setValue()
9691 // to set their value..
9692 (field.store && !field.store.isLocal)
9696 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9697 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9698 field.setFromData(sd);
9700 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9702 field.setFromData(values);
9705 field.setValue(values[id]);
9709 if(this.trackResetOnLoad){
9710 field.originalValue = field.getValue();
9716 //Roo.each(this.childForms || [], function (f) {
9717 // f.setValues(values);
9724 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9725 * they are returned as an array.
9726 * @param {Boolean} asString
9729 getValues : function(asString){
9730 //if (this.childForms) {
9731 // copy values from the child forms
9732 // Roo.each(this.childForms, function (f) {
9733 // this.setValues(f.getValues());
9739 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9740 if(asString === true){
9743 return Roo.urlDecode(fs);
9747 * Returns the fields in this form as an object with key/value pairs.
9748 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9751 getFieldValues : function(with_hidden)
9753 var items = this.getItems();
9755 items.each(function(f){
9761 var v = f.getValue();
9763 if (f.inputType =='radio') {
9764 if (typeof(ret[f.getName()]) == 'undefined') {
9765 ret[f.getName()] = ''; // empty..
9768 if (!f.el.dom.checked) {
9776 if(f.xtype == 'MoneyField'){
9777 ret[f.currencyName] = f.getCurrency();
9780 // not sure if this supported any more..
9781 if ((typeof(v) == 'object') && f.getRawValue) {
9782 v = f.getRawValue() ; // dates..
9784 // combo boxes where name != hiddenName...
9785 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9786 ret[f.name] = f.getRawValue();
9788 ret[f.getName()] = v;
9795 * Clears all invalid messages in this form.
9796 * @return {BasicForm} this
9798 clearInvalid : function(){
9799 var items = this.getItems();
9801 items.each(function(f){
9810 * @return {BasicForm} this
9813 var items = this.getItems();
9814 items.each(function(f){
9818 Roo.each(this.childForms || [], function (f) {
9826 getItems : function()
9828 var r=new Roo.util.MixedCollection(false, function(o){
9829 return o.id || (o.id = Roo.id());
9831 var iter = function(el) {
9838 Roo.each(el.items,function(e) {
9847 hideFields : function(items)
9849 Roo.each(items, function(i){
9851 var f = this.findField(i);
9862 showFields : function(items)
9864 Roo.each(items, function(i){
9866 var f = this.findField(i);
9879 Roo.apply(Roo.bootstrap.Form, {
9906 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9907 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9908 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9909 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9912 this.maskEl.top.enableDisplayMode("block");
9913 this.maskEl.left.enableDisplayMode("block");
9914 this.maskEl.bottom.enableDisplayMode("block");
9915 this.maskEl.right.enableDisplayMode("block");
9917 this.toolTip = new Roo.bootstrap.Tooltip({
9918 cls : 'roo-form-error-popover',
9920 'left' : ['r-l', [-2,0], 'right'],
9921 'right' : ['l-r', [2,0], 'left'],
9922 'bottom' : ['tl-bl', [0,2], 'top'],
9923 'top' : [ 'bl-tl', [0,-2], 'bottom']
9927 this.toolTip.render(Roo.get(document.body));
9929 this.toolTip.el.enableDisplayMode("block");
9931 Roo.get(document.body).on('click', function(){
9935 Roo.get(document.body).on('touchstart', function(){
9939 this.isApplied = true
9942 mask : function(form, target)
9946 this.target = target;
9948 if(!this.form.errorMask || !target.el){
9952 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9954 Roo.log(scrollable);
9956 var ot = this.target.el.calcOffsetsTo(scrollable);
9958 var scrollTo = ot[1] - this.form.maskOffset;
9960 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9962 scrollable.scrollTo('top', scrollTo);
9964 var box = this.target.el.getBox();
9966 var zIndex = Roo.bootstrap.Modal.zIndex++;
9969 this.maskEl.top.setStyle('position', 'absolute');
9970 this.maskEl.top.setStyle('z-index', zIndex);
9971 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9972 this.maskEl.top.setLeft(0);
9973 this.maskEl.top.setTop(0);
9974 this.maskEl.top.show();
9976 this.maskEl.left.setStyle('position', 'absolute');
9977 this.maskEl.left.setStyle('z-index', zIndex);
9978 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9979 this.maskEl.left.setLeft(0);
9980 this.maskEl.left.setTop(box.y - this.padding);
9981 this.maskEl.left.show();
9983 this.maskEl.bottom.setStyle('position', 'absolute');
9984 this.maskEl.bottom.setStyle('z-index', zIndex);
9985 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9986 this.maskEl.bottom.setLeft(0);
9987 this.maskEl.bottom.setTop(box.bottom + this.padding);
9988 this.maskEl.bottom.show();
9990 this.maskEl.right.setStyle('position', 'absolute');
9991 this.maskEl.right.setStyle('z-index', zIndex);
9992 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9993 this.maskEl.right.setLeft(box.right + this.padding);
9994 this.maskEl.right.setTop(box.y - this.padding);
9995 this.maskEl.right.show();
9997 this.toolTip.bindEl = this.target.el;
9999 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10001 var tip = this.target.blankText;
10003 if(this.target.getValue() !== '' ) {
10005 if (this.target.invalidText.length) {
10006 tip = this.target.invalidText;
10007 } else if (this.target.regexText.length){
10008 tip = this.target.regexText;
10012 this.toolTip.show(tip);
10014 this.intervalID = window.setInterval(function() {
10015 Roo.bootstrap.Form.popover.unmask();
10018 window.onwheel = function(){ return false;};
10020 (function(){ this.isMasked = true; }).defer(500, this);
10024 unmask : function()
10026 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10030 this.maskEl.top.setStyle('position', 'absolute');
10031 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10032 this.maskEl.top.hide();
10034 this.maskEl.left.setStyle('position', 'absolute');
10035 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10036 this.maskEl.left.hide();
10038 this.maskEl.bottom.setStyle('position', 'absolute');
10039 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10040 this.maskEl.bottom.hide();
10042 this.maskEl.right.setStyle('position', 'absolute');
10043 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10044 this.maskEl.right.hide();
10046 this.toolTip.hide();
10048 this.toolTip.el.hide();
10050 window.onwheel = function(){ return true;};
10052 if(this.intervalID){
10053 window.clearInterval(this.intervalID);
10054 this.intervalID = false;
10057 this.isMasked = false;
10067 * Ext JS Library 1.1.1
10068 * Copyright(c) 2006-2007, Ext JS, LLC.
10070 * Originally Released Under LGPL - original licence link has changed is not relivant.
10073 * <script type="text/javascript">
10076 * @class Roo.form.VTypes
10077 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10080 Roo.form.VTypes = function(){
10081 // closure these in so they are only created once.
10082 var alpha = /^[a-zA-Z_]+$/;
10083 var alphanum = /^[a-zA-Z0-9_]+$/;
10084 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10085 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10087 // All these messages and functions are configurable
10090 * The function used to validate email addresses
10091 * @param {String} value The email address
10093 'email' : function(v){
10094 return email.test(v);
10097 * The error text to display when the email validation function returns false
10100 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10102 * The keystroke filter mask to be applied on email input
10105 'emailMask' : /[a-z0-9_\.\-@]/i,
10108 * The function used to validate URLs
10109 * @param {String} value The URL
10111 'url' : function(v){
10112 return url.test(v);
10115 * The error text to display when the url validation function returns false
10118 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10121 * The function used to validate alpha values
10122 * @param {String} value The value
10124 'alpha' : function(v){
10125 return alpha.test(v);
10128 * The error text to display when the alpha validation function returns false
10131 'alphaText' : 'This field should only contain letters and _',
10133 * The keystroke filter mask to be applied on alpha input
10136 'alphaMask' : /[a-z_]/i,
10139 * The function used to validate alphanumeric values
10140 * @param {String} value The value
10142 'alphanum' : function(v){
10143 return alphanum.test(v);
10146 * The error text to display when the alphanumeric validation function returns false
10149 'alphanumText' : 'This field should only contain letters, numbers and _',
10151 * The keystroke filter mask to be applied on alphanumeric input
10154 'alphanumMask' : /[a-z0-9_]/i
10164 * @class Roo.bootstrap.Input
10165 * @extends Roo.bootstrap.Component
10166 * Bootstrap Input class
10167 * @cfg {Boolean} disabled is it disabled
10168 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10169 * @cfg {String} name name of the input
10170 * @cfg {string} fieldLabel - the label associated
10171 * @cfg {string} placeholder - placeholder to put in text.
10172 * @cfg {string} before - input group add on before
10173 * @cfg {string} after - input group add on after
10174 * @cfg {string} size - (lg|sm) or leave empty..
10175 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10176 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10177 * @cfg {Number} md colspan out of 12 for computer-sized screens
10178 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10179 * @cfg {string} value default value of the input
10180 * @cfg {Number} labelWidth set the width of label
10181 * @cfg {Number} labellg set the width of label (1-12)
10182 * @cfg {Number} labelmd set the width of label (1-12)
10183 * @cfg {Number} labelsm set the width of label (1-12)
10184 * @cfg {Number} labelxs set the width of label (1-12)
10185 * @cfg {String} labelAlign (top|left)
10186 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10187 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10188 * @cfg {String} indicatorpos (left|right) default left
10189 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10190 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10191 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10193 * @cfg {String} align (left|center|right) Default left
10194 * @cfg {Boolean} forceFeedback (true|false) Default false
10197 * Create a new Input
10198 * @param {Object} config The config object
10201 Roo.bootstrap.Input = function(config){
10203 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10208 * Fires when this field receives input focus.
10209 * @param {Roo.form.Field} this
10214 * Fires when this field loses input focus.
10215 * @param {Roo.form.Field} this
10219 * @event specialkey
10220 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10221 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10222 * @param {Roo.form.Field} this
10223 * @param {Roo.EventObject} e The event object
10228 * Fires just before the field blurs if the field value has changed.
10229 * @param {Roo.form.Field} this
10230 * @param {Mixed} newValue The new value
10231 * @param {Mixed} oldValue The original value
10236 * Fires after the field has been marked as invalid.
10237 * @param {Roo.form.Field} this
10238 * @param {String} msg The validation message
10243 * Fires after the field has been validated with no errors.
10244 * @param {Roo.form.Field} this
10249 * Fires after the key up
10250 * @param {Roo.form.Field} this
10251 * @param {Roo.EventObject} e The event Object
10257 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10259 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10260 automatic validation (defaults to "keyup").
10262 validationEvent : "keyup",
10264 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10266 validateOnBlur : true,
10268 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10270 validationDelay : 250,
10272 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10274 focusClass : "x-form-focus", // not needed???
10278 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10280 invalidClass : "has-warning",
10283 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10285 validClass : "has-success",
10288 * @cfg {Boolean} hasFeedback (true|false) default true
10290 hasFeedback : true,
10293 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10295 invalidFeedbackClass : "glyphicon-warning-sign",
10298 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10300 validFeedbackClass : "glyphicon-ok",
10303 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10305 selectOnFocus : false,
10308 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10312 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10317 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10319 disableKeyFilter : false,
10322 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10326 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10330 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10332 blankText : "Please complete this mandatory field",
10335 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10339 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10341 maxLength : Number.MAX_VALUE,
10343 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10345 minLengthText : "The minimum length for this field is {0}",
10347 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10349 maxLengthText : "The maximum length for this field is {0}",
10353 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10354 * If available, this function will be called only after the basic validators all return true, and will be passed the
10355 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10359 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10360 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10361 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10365 * @cfg {String} regexText -- Depricated - use Invalid Text
10370 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10376 autocomplete: false,
10380 inputType : 'text',
10383 placeholder: false,
10388 preventMark: false,
10389 isFormField : true,
10392 labelAlign : false,
10395 formatedValue : false,
10396 forceFeedback : false,
10398 indicatorpos : 'left',
10408 parentLabelAlign : function()
10411 while (parent.parent()) {
10412 parent = parent.parent();
10413 if (typeof(parent.labelAlign) !='undefined') {
10414 return parent.labelAlign;
10421 getAutoCreate : function()
10423 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10429 if(this.inputType != 'hidden'){
10430 cfg.cls = 'form-group' //input-group
10436 type : this.inputType,
10437 value : this.value,
10438 cls : 'form-control',
10439 placeholder : this.placeholder || '',
10440 autocomplete : this.autocomplete || 'new-password'
10443 if(this.capture.length){
10444 input.capture = this.capture;
10447 if(this.accept.length){
10448 input.accept = this.accept + "/*";
10452 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10455 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10456 input.maxLength = this.maxLength;
10459 if (this.disabled) {
10460 input.disabled=true;
10463 if (this.readOnly) {
10464 input.readonly=true;
10468 input.name = this.name;
10472 input.cls += ' input-' + this.size;
10476 ['xs','sm','md','lg'].map(function(size){
10477 if (settings[size]) {
10478 cfg.cls += ' col-' + size + '-' + settings[size];
10482 var inputblock = input;
10486 cls: 'glyphicon form-control-feedback'
10489 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10492 cls : 'has-feedback',
10500 if (this.before || this.after) {
10503 cls : 'input-group',
10507 if (this.before && typeof(this.before) == 'string') {
10509 inputblock.cn.push({
10511 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10515 if (this.before && typeof(this.before) == 'object') {
10516 this.before = Roo.factory(this.before);
10518 inputblock.cn.push({
10520 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10521 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10525 inputblock.cn.push(input);
10527 if (this.after && typeof(this.after) == 'string') {
10528 inputblock.cn.push({
10530 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10534 if (this.after && typeof(this.after) == 'object') {
10535 this.after = Roo.factory(this.after);
10537 inputblock.cn.push({
10539 cls : 'roo-input-after input-group-append input-group-text input-group-' +
10540 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10544 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10545 inputblock.cls += ' has-feedback';
10546 inputblock.cn.push(feedback);
10551 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10552 tooltip : 'This field is required'
10554 if (Roo.bootstrap.version == 4) {
10557 style : 'display-none'
10560 if (align ==='left' && this.fieldLabel.length) {
10562 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10569 cls : 'control-label col-form-label',
10570 html : this.fieldLabel
10581 var labelCfg = cfg.cn[1];
10582 var contentCfg = cfg.cn[2];
10584 if(this.indicatorpos == 'right'){
10589 cls : 'control-label col-form-label',
10593 html : this.fieldLabel
10607 labelCfg = cfg.cn[0];
10608 contentCfg = cfg.cn[1];
10612 if(this.labelWidth > 12){
10613 labelCfg.style = "width: " + this.labelWidth + 'px';
10616 if(this.labelWidth < 13 && this.labelmd == 0){
10617 this.labelmd = this.labelWidth;
10620 if(this.labellg > 0){
10621 labelCfg.cls += ' col-lg-' + this.labellg;
10622 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10625 if(this.labelmd > 0){
10626 labelCfg.cls += ' col-md-' + this.labelmd;
10627 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10630 if(this.labelsm > 0){
10631 labelCfg.cls += ' col-sm-' + this.labelsm;
10632 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10635 if(this.labelxs > 0){
10636 labelCfg.cls += ' col-xs-' + this.labelxs;
10637 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10641 } else if ( this.fieldLabel.length) {
10646 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10647 tooltip : 'This field is required'
10651 //cls : 'input-group-addon',
10652 html : this.fieldLabel
10660 if(this.indicatorpos == 'right'){
10665 //cls : 'input-group-addon',
10666 html : this.fieldLabel
10671 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10672 tooltip : 'This field is required'
10692 if (this.parentType === 'Navbar' && this.parent().bar) {
10693 cfg.cls += ' navbar-form';
10696 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10697 // on BS4 we do this only if not form
10698 cfg.cls += ' navbar-form';
10706 * return the real input element.
10708 inputEl: function ()
10710 return this.el.select('input.form-control',true).first();
10713 tooltipEl : function()
10715 return this.inputEl();
10718 indicatorEl : function()
10720 if (Roo.bootstrap.version == 4) {
10721 return false; // not enabled in v4 yet.
10724 var indicator = this.el.select('i.roo-required-indicator',true).first();
10734 setDisabled : function(v)
10736 var i = this.inputEl().dom;
10738 i.removeAttribute('disabled');
10742 i.setAttribute('disabled','true');
10744 initEvents : function()
10747 this.inputEl().on("keydown" , this.fireKey, this);
10748 this.inputEl().on("focus", this.onFocus, this);
10749 this.inputEl().on("blur", this.onBlur, this);
10751 this.inputEl().relayEvent('keyup', this);
10753 this.indicator = this.indicatorEl();
10755 if(this.indicator){
10756 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10759 // reference to original value for reset
10760 this.originalValue = this.getValue();
10761 //Roo.form.TextField.superclass.initEvents.call(this);
10762 if(this.validationEvent == 'keyup'){
10763 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10764 this.inputEl().on('keyup', this.filterValidation, this);
10766 else if(this.validationEvent !== false){
10767 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10770 if(this.selectOnFocus){
10771 this.on("focus", this.preFocus, this);
10774 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10775 this.inputEl().on("keypress", this.filterKeys, this);
10777 this.inputEl().relayEvent('keypress', this);
10780 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10781 this.el.on("click", this.autoSize, this);
10784 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10785 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10788 if (typeof(this.before) == 'object') {
10789 this.before.render(this.el.select('.roo-input-before',true).first());
10791 if (typeof(this.after) == 'object') {
10792 this.after.render(this.el.select('.roo-input-after',true).first());
10795 this.inputEl().on('change', this.onChange, this);
10798 filterValidation : function(e){
10799 if(!e.isNavKeyPress()){
10800 this.validationTask.delay(this.validationDelay);
10804 * Validates the field value
10805 * @return {Boolean} True if the value is valid, else false
10807 validate : function(){
10808 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10809 if(this.disabled || this.validateValue(this.getRawValue())){
10814 this.markInvalid();
10820 * Validates a value according to the field's validation rules and marks the field as invalid
10821 * if the validation fails
10822 * @param {Mixed} value The value to validate
10823 * @return {Boolean} True if the value is valid, else false
10825 validateValue : function(value)
10827 if(this.getVisibilityEl().hasClass('hidden')){
10831 if(value.length < 1) { // if it's blank
10832 if(this.allowBlank){
10838 if(value.length < this.minLength){
10841 if(value.length > this.maxLength){
10845 var vt = Roo.form.VTypes;
10846 if(!vt[this.vtype](value, this)){
10850 if(typeof this.validator == "function"){
10851 var msg = this.validator(value);
10855 if (typeof(msg) == 'string') {
10856 this.invalidText = msg;
10860 if(this.regex && !this.regex.test(value)){
10868 fireKey : function(e){
10869 //Roo.log('field ' + e.getKey());
10870 if(e.isNavKeyPress()){
10871 this.fireEvent("specialkey", this, e);
10874 focus : function (selectText){
10876 this.inputEl().focus();
10877 if(selectText === true){
10878 this.inputEl().dom.select();
10884 onFocus : function(){
10885 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10886 // this.el.addClass(this.focusClass);
10888 if(!this.hasFocus){
10889 this.hasFocus = true;
10890 this.startValue = this.getValue();
10891 this.fireEvent("focus", this);
10895 beforeBlur : Roo.emptyFn,
10899 onBlur : function(){
10901 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10902 //this.el.removeClass(this.focusClass);
10904 this.hasFocus = false;
10905 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10908 var v = this.getValue();
10909 if(String(v) !== String(this.startValue)){
10910 this.fireEvent('change', this, v, this.startValue);
10912 this.fireEvent("blur", this);
10915 onChange : function(e)
10917 var v = this.getValue();
10918 if(String(v) !== String(this.startValue)){
10919 this.fireEvent('change', this, v, this.startValue);
10925 * Resets the current field value to the originally loaded value and clears any validation messages
10927 reset : function(){
10928 this.setValue(this.originalValue);
10932 * Returns the name of the field
10933 * @return {Mixed} name The name field
10935 getName: function(){
10939 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
10940 * @return {Mixed} value The field value
10942 getValue : function(){
10944 var v = this.inputEl().getValue();
10949 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10950 * @return {Mixed} value The field value
10952 getRawValue : function(){
10953 var v = this.inputEl().getValue();
10959 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10960 * @param {Mixed} value The value to set
10962 setRawValue : function(v){
10963 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10966 selectText : function(start, end){
10967 var v = this.getRawValue();
10969 start = start === undefined ? 0 : start;
10970 end = end === undefined ? v.length : end;
10971 var d = this.inputEl().dom;
10972 if(d.setSelectionRange){
10973 d.setSelectionRange(start, end);
10974 }else if(d.createTextRange){
10975 var range = d.createTextRange();
10976 range.moveStart("character", start);
10977 range.moveEnd("character", v.length-end);
10984 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10985 * @param {Mixed} value The value to set
10987 setValue : function(v){
10990 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10996 processValue : function(value){
10997 if(this.stripCharsRe){
10998 var newValue = value.replace(this.stripCharsRe, '');
10999 if(newValue !== value){
11000 this.setRawValue(newValue);
11007 preFocus : function(){
11009 if(this.selectOnFocus){
11010 this.inputEl().dom.select();
11013 filterKeys : function(e){
11014 var k = e.getKey();
11015 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11018 var c = e.getCharCode(), cc = String.fromCharCode(c);
11019 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11022 if(!this.maskRe.test(cc)){
11027 * Clear any invalid styles/messages for this field
11029 clearInvalid : function(){
11031 if(!this.el || this.preventMark){ // not rendered
11036 this.el.removeClass([this.invalidClass, 'is-invalid']);
11038 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11040 var feedback = this.el.select('.form-control-feedback', true).first();
11043 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11048 if(this.indicator){
11049 this.indicator.removeClass('visible');
11050 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11053 this.fireEvent('valid', this);
11057 * Mark this field as valid
11059 markValid : function()
11061 if(!this.el || this.preventMark){ // not rendered...
11065 this.el.removeClass([this.invalidClass, this.validClass]);
11066 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11068 var feedback = this.el.select('.form-control-feedback', true).first();
11071 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11074 if(this.indicator){
11075 this.indicator.removeClass('visible');
11076 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11084 if(this.allowBlank && !this.getRawValue().length){
11087 if (Roo.bootstrap.version == 3) {
11088 this.el.addClass(this.validClass);
11090 this.inputEl().addClass('is-valid');
11093 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11095 var feedback = this.el.select('.form-control-feedback', true).first();
11098 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11099 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11104 this.fireEvent('valid', this);
11108 * Mark this field as invalid
11109 * @param {String} msg The validation message
11111 markInvalid : function(msg)
11113 if(!this.el || this.preventMark){ // not rendered
11117 this.el.removeClass([this.invalidClass, this.validClass]);
11118 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11120 var feedback = this.el.select('.form-control-feedback', true).first();
11123 this.el.select('.form-control-feedback', true).first().removeClass(
11124 [this.invalidFeedbackClass, this.validFeedbackClass]);
11131 if(this.allowBlank && !this.getRawValue().length){
11135 if(this.indicator){
11136 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11137 this.indicator.addClass('visible');
11139 if (Roo.bootstrap.version == 3) {
11140 this.el.addClass(this.invalidClass);
11142 this.inputEl().addClass('is-invalid');
11147 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11149 var feedback = this.el.select('.form-control-feedback', true).first();
11152 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11154 if(this.getValue().length || this.forceFeedback){
11155 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11162 this.fireEvent('invalid', this, msg);
11165 SafariOnKeyDown : function(event)
11167 // this is a workaround for a password hang bug on chrome/ webkit.
11168 if (this.inputEl().dom.type != 'password') {
11172 var isSelectAll = false;
11174 if(this.inputEl().dom.selectionEnd > 0){
11175 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11177 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11178 event.preventDefault();
11183 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11185 event.preventDefault();
11186 // this is very hacky as keydown always get's upper case.
11188 var cc = String.fromCharCode(event.getCharCode());
11189 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11193 adjustWidth : function(tag, w){
11194 tag = tag.toLowerCase();
11195 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11196 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11197 if(tag == 'input'){
11200 if(tag == 'textarea'){
11203 }else if(Roo.isOpera){
11204 if(tag == 'input'){
11207 if(tag == 'textarea'){
11215 setFieldLabel : function(v)
11217 if(!this.rendered){
11221 if(this.indicatorEl()){
11222 var ar = this.el.select('label > span',true);
11224 if (ar.elements.length) {
11225 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11226 this.fieldLabel = v;
11230 var br = this.el.select('label',true);
11232 if(br.elements.length) {
11233 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11234 this.fieldLabel = v;
11238 Roo.log('Cannot Found any of label > span || label in input');
11242 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11243 this.fieldLabel = v;
11258 * @class Roo.bootstrap.TextArea
11259 * @extends Roo.bootstrap.Input
11260 * Bootstrap TextArea class
11261 * @cfg {Number} cols Specifies the visible width of a text area
11262 * @cfg {Number} rows Specifies the visible number of lines in a text area
11263 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11264 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11265 * @cfg {string} html text
11268 * Create a new TextArea
11269 * @param {Object} config The config object
11272 Roo.bootstrap.TextArea = function(config){
11273 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11277 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11287 getAutoCreate : function(){
11289 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11295 if(this.inputType != 'hidden'){
11296 cfg.cls = 'form-group' //input-group
11304 value : this.value || '',
11305 html: this.html || '',
11306 cls : 'form-control',
11307 placeholder : this.placeholder || ''
11311 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11312 input.maxLength = this.maxLength;
11316 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11320 input.cols = this.cols;
11323 if (this.readOnly) {
11324 input.readonly = true;
11328 input.name = this.name;
11332 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11336 ['xs','sm','md','lg'].map(function(size){
11337 if (settings[size]) {
11338 cfg.cls += ' col-' + size + '-' + settings[size];
11342 var inputblock = input;
11344 if(this.hasFeedback && !this.allowBlank){
11348 cls: 'glyphicon form-control-feedback'
11352 cls : 'has-feedback',
11361 if (this.before || this.after) {
11364 cls : 'input-group',
11368 inputblock.cn.push({
11370 cls : 'input-group-addon',
11375 inputblock.cn.push(input);
11377 if(this.hasFeedback && !this.allowBlank){
11378 inputblock.cls += ' has-feedback';
11379 inputblock.cn.push(feedback);
11383 inputblock.cn.push({
11385 cls : 'input-group-addon',
11392 if (align ==='left' && this.fieldLabel.length) {
11397 cls : 'control-label',
11398 html : this.fieldLabel
11409 if(this.labelWidth > 12){
11410 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11413 if(this.labelWidth < 13 && this.labelmd == 0){
11414 this.labelmd = this.labelWidth;
11417 if(this.labellg > 0){
11418 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11419 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11422 if(this.labelmd > 0){
11423 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11424 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11427 if(this.labelsm > 0){
11428 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11429 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11432 if(this.labelxs > 0){
11433 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11434 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11437 } else if ( this.fieldLabel.length) {
11442 //cls : 'input-group-addon',
11443 html : this.fieldLabel
11461 if (this.disabled) {
11462 input.disabled=true;
11469 * return the real textarea element.
11471 inputEl: function ()
11473 return this.el.select('textarea.form-control',true).first();
11477 * Clear any invalid styles/messages for this field
11479 clearInvalid : function()
11482 if(!this.el || this.preventMark){ // not rendered
11486 var label = this.el.select('label', true).first();
11487 var icon = this.el.select('i.fa-star', true).first();
11492 this.el.removeClass( this.validClass);
11493 this.inputEl().removeClass('is-invalid');
11495 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11497 var feedback = this.el.select('.form-control-feedback', true).first();
11500 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11505 this.fireEvent('valid', this);
11509 * Mark this field as valid
11511 markValid : function()
11513 if(!this.el || this.preventMark){ // not rendered
11517 this.el.removeClass([this.invalidClass, this.validClass]);
11518 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11520 var feedback = this.el.select('.form-control-feedback', true).first();
11523 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11526 if(this.disabled || this.allowBlank){
11530 var label = this.el.select('label', true).first();
11531 var icon = this.el.select('i.fa-star', true).first();
11536 if (Roo.bootstrap.version == 3) {
11537 this.el.addClass(this.validClass);
11539 this.inputEl().addClass('is-valid');
11543 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11545 var feedback = this.el.select('.form-control-feedback', true).first();
11548 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11549 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11554 this.fireEvent('valid', this);
11558 * Mark this field as invalid
11559 * @param {String} msg The validation message
11561 markInvalid : function(msg)
11563 if(!this.el || this.preventMark){ // not rendered
11567 this.el.removeClass([this.invalidClass, this.validClass]);
11568 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11570 var feedback = this.el.select('.form-control-feedback', true).first();
11573 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11576 if(this.disabled || this.allowBlank){
11580 var label = this.el.select('label', true).first();
11581 var icon = this.el.select('i.fa-star', true).first();
11583 if(!this.getValue().length && label && !icon){
11584 this.el.createChild({
11586 cls : 'text-danger fa fa-lg fa-star',
11587 tooltip : 'This field is required',
11588 style : 'margin-right:5px;'
11592 if (Roo.bootstrap.version == 3) {
11593 this.el.addClass(this.invalidClass);
11595 this.inputEl().addClass('is-invalid');
11598 // fixme ... this may be depricated need to test..
11599 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11601 var feedback = this.el.select('.form-control-feedback', true).first();
11604 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11606 if(this.getValue().length || this.forceFeedback){
11607 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11614 this.fireEvent('invalid', this, msg);
11622 * trigger field - base class for combo..
11627 * @class Roo.bootstrap.TriggerField
11628 * @extends Roo.bootstrap.Input
11629 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11630 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11631 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11632 * for which you can provide a custom implementation. For example:
11634 var trigger = new Roo.bootstrap.TriggerField();
11635 trigger.onTriggerClick = myTriggerFn;
11636 trigger.applyTo('my-field');
11639 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11640 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11641 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11642 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11643 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11646 * Create a new TriggerField.
11647 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11648 * to the base TextField)
11650 Roo.bootstrap.TriggerField = function(config){
11651 this.mimicing = false;
11652 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11655 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11657 * @cfg {String} triggerClass A CSS class to apply to the trigger
11660 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11665 * @cfg {Boolean} removable (true|false) special filter default false
11669 /** @cfg {Boolean} grow @hide */
11670 /** @cfg {Number} growMin @hide */
11671 /** @cfg {Number} growMax @hide */
11677 autoSize: Roo.emptyFn,
11681 deferHeight : true,
11684 actionMode : 'wrap',
11689 getAutoCreate : function(){
11691 var align = this.labelAlign || this.parentLabelAlign();
11696 cls: 'form-group' //input-group
11703 type : this.inputType,
11704 cls : 'form-control',
11705 autocomplete: 'new-password',
11706 placeholder : this.placeholder || ''
11710 input.name = this.name;
11713 input.cls += ' input-' + this.size;
11716 if (this.disabled) {
11717 input.disabled=true;
11720 var inputblock = input;
11722 if(this.hasFeedback && !this.allowBlank){
11726 cls: 'glyphicon form-control-feedback'
11729 if(this.removable && !this.editable ){
11731 cls : 'has-feedback',
11737 cls : 'roo-combo-removable-btn close'
11744 cls : 'has-feedback',
11753 if(this.removable && !this.editable ){
11755 cls : 'roo-removable',
11761 cls : 'roo-combo-removable-btn close'
11768 if (this.before || this.after) {
11771 cls : 'input-group',
11775 inputblock.cn.push({
11777 cls : 'input-group-addon input-group-prepend input-group-text',
11782 inputblock.cn.push(input);
11784 if(this.hasFeedback && !this.allowBlank){
11785 inputblock.cls += ' has-feedback';
11786 inputblock.cn.push(feedback);
11790 inputblock.cn.push({
11792 cls : 'input-group-addon input-group-append input-group-text',
11801 var ibwrap = inputblock;
11806 cls: 'roo-select2-choices',
11810 cls: 'roo-select2-search-field',
11822 cls: 'roo-select2-container input-group',
11827 cls: 'form-hidden-field'
11833 if(!this.multiple && this.showToggleBtn){
11839 if (this.caret != false) {
11842 cls: 'fa fa-' + this.caret
11849 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11851 Roo.bootstrap.version == 3 ? caret : '',
11854 cls: 'combobox-clear',
11868 combobox.cls += ' roo-select2-container-multi';
11872 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11873 tooltip : 'This field is required'
11875 if (Roo.bootstrap.version == 4) {
11878 style : 'display:none'
11883 if (align ==='left' && this.fieldLabel.length) {
11885 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11892 cls : 'control-label',
11893 html : this.fieldLabel
11905 var labelCfg = cfg.cn[1];
11906 var contentCfg = cfg.cn[2];
11908 if(this.indicatorpos == 'right'){
11913 cls : 'control-label',
11917 html : this.fieldLabel
11931 labelCfg = cfg.cn[0];
11932 contentCfg = cfg.cn[1];
11935 if(this.labelWidth > 12){
11936 labelCfg.style = "width: " + this.labelWidth + 'px';
11939 if(this.labelWidth < 13 && this.labelmd == 0){
11940 this.labelmd = this.labelWidth;
11943 if(this.labellg > 0){
11944 labelCfg.cls += ' col-lg-' + this.labellg;
11945 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11948 if(this.labelmd > 0){
11949 labelCfg.cls += ' col-md-' + this.labelmd;
11950 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11953 if(this.labelsm > 0){
11954 labelCfg.cls += ' col-sm-' + this.labelsm;
11955 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11958 if(this.labelxs > 0){
11959 labelCfg.cls += ' col-xs-' + this.labelxs;
11960 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11963 } else if ( this.fieldLabel.length) {
11964 // Roo.log(" label");
11969 //cls : 'input-group-addon',
11970 html : this.fieldLabel
11978 if(this.indicatorpos == 'right'){
11986 html : this.fieldLabel
12000 // Roo.log(" no label && no align");
12007 ['xs','sm','md','lg'].map(function(size){
12008 if (settings[size]) {
12009 cfg.cls += ' col-' + size + '-' + settings[size];
12020 onResize : function(w, h){
12021 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12022 // if(typeof w == 'number'){
12023 // var x = w - this.trigger.getWidth();
12024 // this.inputEl().setWidth(this.adjustWidth('input', x));
12025 // this.trigger.setStyle('left', x+'px');
12030 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12033 getResizeEl : function(){
12034 return this.inputEl();
12038 getPositionEl : function(){
12039 return this.inputEl();
12043 alignErrorIcon : function(){
12044 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12048 initEvents : function(){
12052 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12053 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12054 if(!this.multiple && this.showToggleBtn){
12055 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12056 if(this.hideTrigger){
12057 this.trigger.setDisplayed(false);
12059 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12063 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12066 if(this.removable && !this.editable && !this.tickable){
12067 var close = this.closeTriggerEl();
12070 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12071 close.on('click', this.removeBtnClick, this, close);
12075 //this.trigger.addClassOnOver('x-form-trigger-over');
12076 //this.trigger.addClassOnClick('x-form-trigger-click');
12079 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12083 closeTriggerEl : function()
12085 var close = this.el.select('.roo-combo-removable-btn', true).first();
12086 return close ? close : false;
12089 removeBtnClick : function(e, h, el)
12091 e.preventDefault();
12093 if(this.fireEvent("remove", this) !== false){
12095 this.fireEvent("afterremove", this)
12099 createList : function()
12101 this.list = Roo.get(document.body).createChild({
12102 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12103 cls: 'typeahead typeahead-long dropdown-menu',
12104 style: 'display:none'
12107 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12112 initTrigger : function(){
12117 onDestroy : function(){
12119 this.trigger.removeAllListeners();
12120 // this.trigger.remove();
12123 // this.wrap.remove();
12125 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12129 onFocus : function(){
12130 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12132 if(!this.mimicing){
12133 this.wrap.addClass('x-trigger-wrap-focus');
12134 this.mimicing = true;
12135 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12136 if(this.monitorTab){
12137 this.el.on("keydown", this.checkTab, this);
12144 checkTab : function(e){
12145 if(e.getKey() == e.TAB){
12146 this.triggerBlur();
12151 onBlur : function(){
12156 mimicBlur : function(e, t){
12158 if(!this.wrap.contains(t) && this.validateBlur()){
12159 this.triggerBlur();
12165 triggerBlur : function(){
12166 this.mimicing = false;
12167 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12168 if(this.monitorTab){
12169 this.el.un("keydown", this.checkTab, this);
12171 //this.wrap.removeClass('x-trigger-wrap-focus');
12172 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12176 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12177 validateBlur : function(e, t){
12182 onDisable : function(){
12183 this.inputEl().dom.disabled = true;
12184 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12186 // this.wrap.addClass('x-item-disabled');
12191 onEnable : function(){
12192 this.inputEl().dom.disabled = false;
12193 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12195 // this.el.removeClass('x-item-disabled');
12200 onShow : function(){
12201 var ae = this.getActionEl();
12204 ae.dom.style.display = '';
12205 ae.dom.style.visibility = 'visible';
12211 onHide : function(){
12212 var ae = this.getActionEl();
12213 ae.dom.style.display = 'none';
12217 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12218 * by an implementing function.
12220 * @param {EventObject} e
12222 onTriggerClick : Roo.emptyFn
12230 * @class Roo.bootstrap.CardUploader
12231 * @extends Roo.bootstrap.Button
12232 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12233 * @cfg {Number} errorTimeout default 3000
12234 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12235 * @cfg {Array} html The button text.
12239 * Create a new CardUploader
12240 * @param {Object} config The config object
12243 Roo.bootstrap.CardUploader = function(config){
12247 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12250 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12257 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12260 errorTimeout : 3000,
12264 fileCollection : false,
12267 getAutoCreate : function()
12271 cls :'form-group' ,
12276 //cls : 'input-group-addon',
12277 html : this.fieldLabel
12284 value : this.value,
12285 cls : 'd-none form-control'
12290 multiple : 'multiple',
12292 cls : 'd-none roo-card-upload-selector'
12296 cls : 'roo-card-uploader-button-container w-100 mb-2'
12299 cls : 'card-columns roo-card-uploader-container'
12309 getChildContainer : function() /// what children are added to.
12311 return this.containerEl;
12314 getButtonContainer : function() /// what children are added to.
12316 return this.el.select(".roo-card-uploader-button-container").first();
12319 initEvents : function()
12322 Roo.bootstrap.Input.prototype.initEvents.call(this);
12326 xns: Roo.bootstrap,
12329 container_method : 'getButtonContainer' ,
12330 html : this.html, // fix changable?
12333 'click' : function(btn, e) {
12342 this.urlAPI = (window.createObjectURL && window) ||
12343 (window.URL && URL.revokeObjectURL && URL) ||
12344 (window.webkitURL && webkitURL);
12349 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12351 this.selectorEl.on('change', this.onFileSelected, this);
12354 this.images.forEach(function(img) {
12357 this.images = false;
12359 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12365 onClick : function(e)
12367 e.preventDefault();
12369 this.selectorEl.dom.click();
12373 onFileSelected : function(e)
12375 e.preventDefault();
12377 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12381 Roo.each(this.selectorEl.dom.files, function(file){
12382 this.addFile(file);
12391 addFile : function(file)
12394 if(typeof(file) === 'string'){
12395 throw "Add file by name?"; // should not happen
12399 if(!file || !this.urlAPI){
12409 var url = _this.urlAPI.createObjectURL( file);
12412 id : Roo.bootstrap.CardUploader.ID--,
12413 is_uploaded : false,
12416 mimetype : file.type,
12423 addCard : function (data)
12425 // hidden input element?
12426 // if the file is not an image...
12427 //then we need to use something other that and header_image
12432 xns : Roo.bootstrap,
12433 xtype : 'CardFooter',
12436 xns : Roo.bootstrap,
12442 xns : Roo.bootstrap,
12444 html : String.format("<small>{0}</small>", data.title),
12445 cls : 'col-11 text-left',
12450 click : function() {
12451 this.downloadCard(data.id)
12457 xns : Roo.bootstrap,
12465 click : function() {
12466 t.removeCard(data.id)
12478 var cn = this.addxtype(
12481 xns : Roo.bootstrap,
12484 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12485 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12486 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12491 initEvents : function() {
12492 Roo.bootstrap.Card.prototype.initEvents.call(this);
12493 this.imgEl = this.el.select('.card-img-top').first();
12495 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12496 this.imgEl.set({ 'pointer' : 'cursor' });
12505 // dont' really need ot update items.
12506 // this.items.push(cn);
12507 this.fileCollection.add(cn);
12508 this.updateInput();
12511 removeCard : function(id)
12514 var card = this.fileCollection.get(id);
12515 card.data.is_deleted = 1;
12516 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12517 this.fileCollection.remove(card);
12518 //this.items = this.items.filter(function(e) { return e != card });
12519 // dont' really need ot update items.
12520 card.el.dom.parentNode.removeChild(card.el.dom);
12525 this.fileCollection.each(function(card) {
12526 card.el.dom.parentNode.removeChild(card.el.dom);
12528 this.fileCollection.clear();
12529 this.updateInput();
12532 updateInput : function()
12535 this.fileCollection.each(function(e) {
12539 this.inputEl().dom.value = JSON.stringify(data);
12546 Roo.bootstrap.CardUploader.ID = -1;/*
12548 * Ext JS Library 1.1.1
12549 * Copyright(c) 2006-2007, Ext JS, LLC.
12551 * Originally Released Under LGPL - original licence link has changed is not relivant.
12554 * <script type="text/javascript">
12559 * @class Roo.data.SortTypes
12561 * Defines the default sorting (casting?) comparison functions used when sorting data.
12563 Roo.data.SortTypes = {
12565 * Default sort that does nothing
12566 * @param {Mixed} s The value being converted
12567 * @return {Mixed} The comparison value
12569 none : function(s){
12574 * The regular expression used to strip tags
12578 stripTagsRE : /<\/?[^>]+>/gi,
12581 * Strips all HTML tags to sort on text only
12582 * @param {Mixed} s The value being converted
12583 * @return {String} The comparison value
12585 asText : function(s){
12586 return String(s).replace(this.stripTagsRE, "");
12590 * Strips all HTML tags to sort on text only - Case insensitive
12591 * @param {Mixed} s The value being converted
12592 * @return {String} The comparison value
12594 asUCText : function(s){
12595 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12599 * Case insensitive string
12600 * @param {Mixed} s The value being converted
12601 * @return {String} The comparison value
12603 asUCString : function(s) {
12604 return String(s).toUpperCase();
12609 * @param {Mixed} s The value being converted
12610 * @return {Number} The comparison value
12612 asDate : function(s) {
12616 if(s instanceof Date){
12617 return s.getTime();
12619 return Date.parse(String(s));
12624 * @param {Mixed} s The value being converted
12625 * @return {Float} The comparison value
12627 asFloat : function(s) {
12628 var val = parseFloat(String(s).replace(/,/g, ""));
12637 * @param {Mixed} s The value being converted
12638 * @return {Number} The comparison value
12640 asInt : function(s) {
12641 var val = parseInt(String(s).replace(/,/g, ""));
12649 * Ext JS Library 1.1.1
12650 * Copyright(c) 2006-2007, Ext JS, LLC.
12652 * Originally Released Under LGPL - original licence link has changed is not relivant.
12655 * <script type="text/javascript">
12659 * @class Roo.data.Record
12660 * Instances of this class encapsulate both record <em>definition</em> information, and record
12661 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12662 * to access Records cached in an {@link Roo.data.Store} object.<br>
12664 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12665 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12668 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12670 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12671 * {@link #create}. The parameters are the same.
12672 * @param {Array} data An associative Array of data values keyed by the field name.
12673 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12674 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12675 * not specified an integer id is generated.
12677 Roo.data.Record = function(data, id){
12678 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12683 * Generate a constructor for a specific record layout.
12684 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12685 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12686 * Each field definition object may contain the following properties: <ul>
12687 * <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,
12688 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12689 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12690 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12691 * is being used, then this is a string containing the javascript expression to reference the data relative to
12692 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12693 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12694 * this may be omitted.</p></li>
12695 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12696 * <ul><li>auto (Default, implies no conversion)</li>
12701 * <li>date</li></ul></p></li>
12702 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12703 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12704 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12705 * by the Reader into an object that will be stored in the Record. It is passed the
12706 * following parameters:<ul>
12707 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12709 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12711 * <br>usage:<br><pre><code>
12712 var TopicRecord = Roo.data.Record.create(
12713 {name: 'title', mapping: 'topic_title'},
12714 {name: 'author', mapping: 'username'},
12715 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12716 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12717 {name: 'lastPoster', mapping: 'user2'},
12718 {name: 'excerpt', mapping: 'post_text'}
12721 var myNewRecord = new TopicRecord({
12722 title: 'Do my job please',
12725 lastPost: new Date(),
12726 lastPoster: 'Animal',
12727 excerpt: 'No way dude!'
12729 myStore.add(myNewRecord);
12734 Roo.data.Record.create = function(o){
12735 var f = function(){
12736 f.superclass.constructor.apply(this, arguments);
12738 Roo.extend(f, Roo.data.Record);
12739 var p = f.prototype;
12740 p.fields = new Roo.util.MixedCollection(false, function(field){
12743 for(var i = 0, len = o.length; i < len; i++){
12744 p.fields.add(new Roo.data.Field(o[i]));
12746 f.getField = function(name){
12747 return p.fields.get(name);
12752 Roo.data.Record.AUTO_ID = 1000;
12753 Roo.data.Record.EDIT = 'edit';
12754 Roo.data.Record.REJECT = 'reject';
12755 Roo.data.Record.COMMIT = 'commit';
12757 Roo.data.Record.prototype = {
12759 * Readonly flag - true if this record has been modified.
12768 join : function(store){
12769 this.store = store;
12773 * Set the named field to the specified value.
12774 * @param {String} name The name of the field to set.
12775 * @param {Object} value The value to set the field to.
12777 set : function(name, value){
12778 if(this.data[name] == value){
12782 if(!this.modified){
12783 this.modified = {};
12785 if(typeof this.modified[name] == 'undefined'){
12786 this.modified[name] = this.data[name];
12788 this.data[name] = value;
12789 if(!this.editing && this.store){
12790 this.store.afterEdit(this);
12795 * Get the value of the named field.
12796 * @param {String} name The name of the field to get the value of.
12797 * @return {Object} The value of the field.
12799 get : function(name){
12800 return this.data[name];
12804 beginEdit : function(){
12805 this.editing = true;
12806 this.modified = {};
12810 cancelEdit : function(){
12811 this.editing = false;
12812 delete this.modified;
12816 endEdit : function(){
12817 this.editing = false;
12818 if(this.dirty && this.store){
12819 this.store.afterEdit(this);
12824 * Usually called by the {@link Roo.data.Store} which owns the Record.
12825 * Rejects all changes made to the Record since either creation, or the last commit operation.
12826 * Modified fields are reverted to their original values.
12828 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12829 * of reject operations.
12831 reject : function(){
12832 var m = this.modified;
12834 if(typeof m[n] != "function"){
12835 this.data[n] = m[n];
12838 this.dirty = false;
12839 delete this.modified;
12840 this.editing = false;
12842 this.store.afterReject(this);
12847 * Usually called by the {@link Roo.data.Store} which owns the Record.
12848 * Commits all changes made to the Record since either creation, or the last commit operation.
12850 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12851 * of commit operations.
12853 commit : function(){
12854 this.dirty = false;
12855 delete this.modified;
12856 this.editing = false;
12858 this.store.afterCommit(this);
12863 hasError : function(){
12864 return this.error != null;
12868 clearError : function(){
12873 * Creates a copy of this record.
12874 * @param {String} id (optional) A new record id if you don't want to use this record's id
12877 copy : function(newId) {
12878 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12882 * Ext JS Library 1.1.1
12883 * Copyright(c) 2006-2007, Ext JS, LLC.
12885 * Originally Released Under LGPL - original licence link has changed is not relivant.
12888 * <script type="text/javascript">
12894 * @class Roo.data.Store
12895 * @extends Roo.util.Observable
12896 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12897 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12899 * 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
12900 * has no knowledge of the format of the data returned by the Proxy.<br>
12902 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12903 * instances from the data object. These records are cached and made available through accessor functions.
12905 * Creates a new Store.
12906 * @param {Object} config A config object containing the objects needed for the Store to access data,
12907 * and read the data into Records.
12909 Roo.data.Store = function(config){
12910 this.data = new Roo.util.MixedCollection(false);
12911 this.data.getKey = function(o){
12914 this.baseParams = {};
12916 this.paramNames = {
12921 "multisort" : "_multisort"
12924 if(config && config.data){
12925 this.inlineData = config.data;
12926 delete config.data;
12929 Roo.apply(this, config);
12931 if(this.reader){ // reader passed
12932 this.reader = Roo.factory(this.reader, Roo.data);
12933 this.reader.xmodule = this.xmodule || false;
12934 if(!this.recordType){
12935 this.recordType = this.reader.recordType;
12937 if(this.reader.onMetaChange){
12938 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12942 if(this.recordType){
12943 this.fields = this.recordType.prototype.fields;
12945 this.modified = [];
12949 * @event datachanged
12950 * Fires when the data cache has changed, and a widget which is using this Store
12951 * as a Record cache should refresh its view.
12952 * @param {Store} this
12954 datachanged : true,
12956 * @event metachange
12957 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12958 * @param {Store} this
12959 * @param {Object} meta The JSON metadata
12964 * Fires when Records have been added to the Store
12965 * @param {Store} this
12966 * @param {Roo.data.Record[]} records The array of Records added
12967 * @param {Number} index The index at which the record(s) were added
12972 * Fires when a Record has been removed from the Store
12973 * @param {Store} this
12974 * @param {Roo.data.Record} record The Record that was removed
12975 * @param {Number} index The index at which the record was removed
12980 * Fires when a Record has been updated
12981 * @param {Store} this
12982 * @param {Roo.data.Record} record The Record that was updated
12983 * @param {String} operation The update operation being performed. Value may be one of:
12985 Roo.data.Record.EDIT
12986 Roo.data.Record.REJECT
12987 Roo.data.Record.COMMIT
12993 * Fires when the data cache has been cleared.
12994 * @param {Store} this
12998 * @event beforeload
12999 * Fires before a request is made for a new data object. If the beforeload handler returns false
13000 * the load action will be canceled.
13001 * @param {Store} this
13002 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13006 * @event beforeloadadd
13007 * Fires after a new set of Records has been loaded.
13008 * @param {Store} this
13009 * @param {Roo.data.Record[]} records The Records that were loaded
13010 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13012 beforeloadadd : true,
13015 * Fires after a new set of Records has been loaded, before they are added to the store.
13016 * @param {Store} this
13017 * @param {Roo.data.Record[]} records The Records that were loaded
13018 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13019 * @params {Object} return from reader
13023 * @event loadexception
13024 * Fires if an exception occurs in the Proxy during loading.
13025 * Called with the signature of the Proxy's "loadexception" event.
13026 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13029 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13030 * @param {Object} load options
13031 * @param {Object} jsonData from your request (normally this contains the Exception)
13033 loadexception : true
13037 this.proxy = Roo.factory(this.proxy, Roo.data);
13038 this.proxy.xmodule = this.xmodule || false;
13039 this.relayEvents(this.proxy, ["loadexception"]);
13041 this.sortToggle = {};
13042 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13044 Roo.data.Store.superclass.constructor.call(this);
13046 if(this.inlineData){
13047 this.loadData(this.inlineData);
13048 delete this.inlineData;
13052 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13054 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13055 * without a remote query - used by combo/forms at present.
13059 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13062 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13065 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13066 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13069 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13070 * on any HTTP request
13073 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13076 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13080 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13081 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13083 remoteSort : false,
13086 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13087 * loaded or when a record is removed. (defaults to false).
13089 pruneModifiedRecords : false,
13092 lastOptions : null,
13095 * Add Records to the Store and fires the add event.
13096 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13098 add : function(records){
13099 records = [].concat(records);
13100 for(var i = 0, len = records.length; i < len; i++){
13101 records[i].join(this);
13103 var index = this.data.length;
13104 this.data.addAll(records);
13105 this.fireEvent("add", this, records, index);
13109 * Remove a Record from the Store and fires the remove event.
13110 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13112 remove : function(record){
13113 var index = this.data.indexOf(record);
13114 this.data.removeAt(index);
13116 if(this.pruneModifiedRecords){
13117 this.modified.remove(record);
13119 this.fireEvent("remove", this, record, index);
13123 * Remove all Records from the Store and fires the clear event.
13125 removeAll : function(){
13127 if(this.pruneModifiedRecords){
13128 this.modified = [];
13130 this.fireEvent("clear", this);
13134 * Inserts Records to the Store at the given index and fires the add event.
13135 * @param {Number} index The start index at which to insert the passed Records.
13136 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13138 insert : function(index, records){
13139 records = [].concat(records);
13140 for(var i = 0, len = records.length; i < len; i++){
13141 this.data.insert(index, records[i]);
13142 records[i].join(this);
13144 this.fireEvent("add", this, records, index);
13148 * Get the index within the cache of the passed Record.
13149 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13150 * @return {Number} The index of the passed Record. Returns -1 if not found.
13152 indexOf : function(record){
13153 return this.data.indexOf(record);
13157 * Get the index within the cache of the Record with the passed id.
13158 * @param {String} id The id of the Record to find.
13159 * @return {Number} The index of the Record. Returns -1 if not found.
13161 indexOfId : function(id){
13162 return this.data.indexOfKey(id);
13166 * Get the Record with the specified id.
13167 * @param {String} id The id of the Record to find.
13168 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13170 getById : function(id){
13171 return this.data.key(id);
13175 * Get the Record at the specified index.
13176 * @param {Number} index The index of the Record to find.
13177 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13179 getAt : function(index){
13180 return this.data.itemAt(index);
13184 * Returns a range of Records between specified indices.
13185 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13186 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13187 * @return {Roo.data.Record[]} An array of Records
13189 getRange : function(start, end){
13190 return this.data.getRange(start, end);
13194 storeOptions : function(o){
13195 o = Roo.apply({}, o);
13198 this.lastOptions = o;
13202 * Loads the Record cache from the configured Proxy using the configured Reader.
13204 * If using remote paging, then the first load call must specify the <em>start</em>
13205 * and <em>limit</em> properties in the options.params property to establish the initial
13206 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13208 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13209 * and this call will return before the new data has been loaded. Perform any post-processing
13210 * in a callback function, or in a "load" event handler.</strong>
13212 * @param {Object} options An object containing properties which control loading options:<ul>
13213 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13214 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13215 * passed the following arguments:<ul>
13216 * <li>r : Roo.data.Record[]</li>
13217 * <li>options: Options object from the load call</li>
13218 * <li>success: Boolean success indicator</li></ul></li>
13219 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13220 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13223 load : function(options){
13224 options = options || {};
13225 if(this.fireEvent("beforeload", this, options) !== false){
13226 this.storeOptions(options);
13227 var p = Roo.apply(options.params || {}, this.baseParams);
13228 // if meta was not loaded from remote source.. try requesting it.
13229 if (!this.reader.metaFromRemote) {
13230 p._requestMeta = 1;
13232 if(this.sortInfo && this.remoteSort){
13233 var pn = this.paramNames;
13234 p[pn["sort"]] = this.sortInfo.field;
13235 p[pn["dir"]] = this.sortInfo.direction;
13237 if (this.multiSort) {
13238 var pn = this.paramNames;
13239 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13242 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13247 * Reloads the Record cache from the configured Proxy using the configured Reader and
13248 * the options from the last load operation performed.
13249 * @param {Object} options (optional) An object containing properties which may override the options
13250 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13251 * the most recently used options are reused).
13253 reload : function(options){
13254 this.load(Roo.applyIf(options||{}, this.lastOptions));
13258 // Called as a callback by the Reader during a load operation.
13259 loadRecords : function(o, options, success){
13260 if(!o || success === false){
13261 if(success !== false){
13262 this.fireEvent("load", this, [], options, o);
13264 if(options.callback){
13265 options.callback.call(options.scope || this, [], options, false);
13269 // if data returned failure - throw an exception.
13270 if (o.success === false) {
13271 // show a message if no listener is registered.
13272 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13273 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13275 // loadmask wil be hooked into this..
13276 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13279 var r = o.records, t = o.totalRecords || r.length;
13281 this.fireEvent("beforeloadadd", this, r, options, o);
13283 if(!options || options.add !== true){
13284 if(this.pruneModifiedRecords){
13285 this.modified = [];
13287 for(var i = 0, len = r.length; i < len; i++){
13291 this.data = this.snapshot;
13292 delete this.snapshot;
13295 this.data.addAll(r);
13296 this.totalLength = t;
13298 this.fireEvent("datachanged", this);
13300 this.totalLength = Math.max(t, this.data.length+r.length);
13304 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13306 var e = new Roo.data.Record({});
13308 e.set(this.parent.displayField, this.parent.emptyTitle);
13309 e.set(this.parent.valueField, '');
13314 this.fireEvent("load", this, r, options, o);
13315 if(options.callback){
13316 options.callback.call(options.scope || this, r, options, true);
13322 * Loads data from a passed data block. A Reader which understands the format of the data
13323 * must have been configured in the constructor.
13324 * @param {Object} data The data block from which to read the Records. The format of the data expected
13325 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13326 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13328 loadData : function(o, append){
13329 var r = this.reader.readRecords(o);
13330 this.loadRecords(r, {add: append}, true);
13334 * using 'cn' the nested child reader read the child array into it's child stores.
13335 * @param {Object} rec The record with a 'children array
13337 loadDataFromChildren : function(rec)
13339 this.loadData(this.reader.toLoadData(rec));
13344 * Gets the number of cached records.
13346 * <em>If using paging, this may not be the total size of the dataset. If the data object
13347 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13348 * the data set size</em>
13350 getCount : function(){
13351 return this.data.length || 0;
13355 * Gets the total number of records in the dataset as returned by the server.
13357 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13358 * the dataset size</em>
13360 getTotalCount : function(){
13361 return this.totalLength || 0;
13365 * Returns the sort state of the Store as an object with two properties:
13367 field {String} The name of the field by which the Records are sorted
13368 direction {String} The sort order, "ASC" or "DESC"
13371 getSortState : function(){
13372 return this.sortInfo;
13376 applySort : function(){
13377 if(this.sortInfo && !this.remoteSort){
13378 var s = this.sortInfo, f = s.field;
13379 var st = this.fields.get(f).sortType;
13380 var fn = function(r1, r2){
13381 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13382 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13384 this.data.sort(s.direction, fn);
13385 if(this.snapshot && this.snapshot != this.data){
13386 this.snapshot.sort(s.direction, fn);
13392 * Sets the default sort column and order to be used by the next load operation.
13393 * @param {String} fieldName The name of the field to sort by.
13394 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13396 setDefaultSort : function(field, dir){
13397 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13401 * Sort the Records.
13402 * If remote sorting is used, the sort is performed on the server, and the cache is
13403 * reloaded. If local sorting is used, the cache is sorted internally.
13404 * @param {String} fieldName The name of the field to sort by.
13405 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13407 sort : function(fieldName, dir){
13408 var f = this.fields.get(fieldName);
13410 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13412 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13413 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13418 this.sortToggle[f.name] = dir;
13419 this.sortInfo = {field: f.name, direction: dir};
13420 if(!this.remoteSort){
13422 this.fireEvent("datachanged", this);
13424 this.load(this.lastOptions);
13429 * Calls the specified function for each of the Records in the cache.
13430 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13431 * Returning <em>false</em> aborts and exits the iteration.
13432 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13434 each : function(fn, scope){
13435 this.data.each(fn, scope);
13439 * Gets all records modified since the last commit. Modified records are persisted across load operations
13440 * (e.g., during paging).
13441 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13443 getModifiedRecords : function(){
13444 return this.modified;
13448 createFilterFn : function(property, value, anyMatch){
13449 if(!value.exec){ // not a regex
13450 value = String(value);
13451 if(value.length == 0){
13454 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13456 return function(r){
13457 return value.test(r.data[property]);
13462 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13463 * @param {String} property A field on your records
13464 * @param {Number} start The record index to start at (defaults to 0)
13465 * @param {Number} end The last record index to include (defaults to length - 1)
13466 * @return {Number} The sum
13468 sum : function(property, start, end){
13469 var rs = this.data.items, v = 0;
13470 start = start || 0;
13471 end = (end || end === 0) ? end : rs.length-1;
13473 for(var i = start; i <= end; i++){
13474 v += (rs[i].data[property] || 0);
13480 * Filter the records by a specified property.
13481 * @param {String} field A field on your records
13482 * @param {String/RegExp} value Either a string that the field
13483 * should start with or a RegExp to test against the field
13484 * @param {Boolean} anyMatch True to match any part not just the beginning
13486 filter : function(property, value, anyMatch){
13487 var fn = this.createFilterFn(property, value, anyMatch);
13488 return fn ? this.filterBy(fn) : this.clearFilter();
13492 * Filter by a function. The specified function will be called with each
13493 * record in this data source. If the function returns true the record is included,
13494 * otherwise it is filtered.
13495 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13496 * @param {Object} scope (optional) The scope of the function (defaults to this)
13498 filterBy : function(fn, scope){
13499 this.snapshot = this.snapshot || this.data;
13500 this.data = this.queryBy(fn, scope||this);
13501 this.fireEvent("datachanged", this);
13505 * Query the records by a specified property.
13506 * @param {String} field A field on your records
13507 * @param {String/RegExp} value Either a string that the field
13508 * should start with or a RegExp to test against the field
13509 * @param {Boolean} anyMatch True to match any part not just the beginning
13510 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13512 query : function(property, value, anyMatch){
13513 var fn = this.createFilterFn(property, value, anyMatch);
13514 return fn ? this.queryBy(fn) : this.data.clone();
13518 * Query by a function. The specified function will be called with each
13519 * record in this data source. If the function returns true the record is included
13521 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13522 * @param {Object} scope (optional) The scope of the function (defaults to this)
13523 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13525 queryBy : function(fn, scope){
13526 var data = this.snapshot || this.data;
13527 return data.filterBy(fn, scope||this);
13531 * Collects unique values for a particular dataIndex from this store.
13532 * @param {String} dataIndex The property to collect
13533 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13534 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13535 * @return {Array} An array of the unique values
13537 collect : function(dataIndex, allowNull, bypassFilter){
13538 var d = (bypassFilter === true && this.snapshot) ?
13539 this.snapshot.items : this.data.items;
13540 var v, sv, r = [], l = {};
13541 for(var i = 0, len = d.length; i < len; i++){
13542 v = d[i].data[dataIndex];
13544 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13553 * Revert to a view of the Record cache with no filtering applied.
13554 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13556 clearFilter : function(suppressEvent){
13557 if(this.snapshot && this.snapshot != this.data){
13558 this.data = this.snapshot;
13559 delete this.snapshot;
13560 if(suppressEvent !== true){
13561 this.fireEvent("datachanged", this);
13567 afterEdit : function(record){
13568 if(this.modified.indexOf(record) == -1){
13569 this.modified.push(record);
13571 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13575 afterReject : function(record){
13576 this.modified.remove(record);
13577 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13581 afterCommit : function(record){
13582 this.modified.remove(record);
13583 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13587 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13588 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13590 commitChanges : function(){
13591 var m = this.modified.slice(0);
13592 this.modified = [];
13593 for(var i = 0, len = m.length; i < len; i++){
13599 * Cancel outstanding changes on all changed records.
13601 rejectChanges : function(){
13602 var m = this.modified.slice(0);
13603 this.modified = [];
13604 for(var i = 0, len = m.length; i < len; i++){
13609 onMetaChange : function(meta, rtype, o){
13610 this.recordType = rtype;
13611 this.fields = rtype.prototype.fields;
13612 delete this.snapshot;
13613 this.sortInfo = meta.sortInfo || this.sortInfo;
13614 this.modified = [];
13615 this.fireEvent('metachange', this, this.reader.meta);
13618 moveIndex : function(data, type)
13620 var index = this.indexOf(data);
13622 var newIndex = index + type;
13626 this.insert(newIndex, data);
13631 * Ext JS Library 1.1.1
13632 * Copyright(c) 2006-2007, Ext JS, LLC.
13634 * Originally Released Under LGPL - original licence link has changed is not relivant.
13637 * <script type="text/javascript">
13641 * @class Roo.data.SimpleStore
13642 * @extends Roo.data.Store
13643 * Small helper class to make creating Stores from Array data easier.
13644 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13645 * @cfg {Array} fields An array of field definition objects, or field name strings.
13646 * @cfg {Object} an existing reader (eg. copied from another store)
13647 * @cfg {Array} data The multi-dimensional array of data
13649 * @param {Object} config
13651 Roo.data.SimpleStore = function(config)
13653 Roo.data.SimpleStore.superclass.constructor.call(this, {
13655 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13658 Roo.data.Record.create(config.fields)
13660 proxy : new Roo.data.MemoryProxy(config.data)
13664 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13666 * Ext JS Library 1.1.1
13667 * Copyright(c) 2006-2007, Ext JS, LLC.
13669 * Originally Released Under LGPL - original licence link has changed is not relivant.
13672 * <script type="text/javascript">
13677 * @extends Roo.data.Store
13678 * @class Roo.data.JsonStore
13679 * Small helper class to make creating Stores for JSON data easier. <br/>
13681 var store = new Roo.data.JsonStore({
13682 url: 'get-images.php',
13684 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13687 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13688 * JsonReader and HttpProxy (unless inline data is provided).</b>
13689 * @cfg {Array} fields An array of field definition objects, or field name strings.
13691 * @param {Object} config
13693 Roo.data.JsonStore = function(c){
13694 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13695 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13696 reader: new Roo.data.JsonReader(c, c.fields)
13699 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13701 * Ext JS Library 1.1.1
13702 * Copyright(c) 2006-2007, Ext JS, LLC.
13704 * Originally Released Under LGPL - original licence link has changed is not relivant.
13707 * <script type="text/javascript">
13711 Roo.data.Field = function(config){
13712 if(typeof config == "string"){
13713 config = {name: config};
13715 Roo.apply(this, config);
13718 this.type = "auto";
13721 var st = Roo.data.SortTypes;
13722 // named sortTypes are supported, here we look them up
13723 if(typeof this.sortType == "string"){
13724 this.sortType = st[this.sortType];
13727 // set default sortType for strings and dates
13728 if(!this.sortType){
13731 this.sortType = st.asUCString;
13734 this.sortType = st.asDate;
13737 this.sortType = st.none;
13742 var stripRe = /[\$,%]/g;
13744 // prebuilt conversion function for this field, instead of
13745 // switching every time we're reading a value
13747 var cv, dateFormat = this.dateFormat;
13752 cv = function(v){ return v; };
13755 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13759 return v !== undefined && v !== null && v !== '' ?
13760 parseInt(String(v).replace(stripRe, ""), 10) : '';
13765 return v !== undefined && v !== null && v !== '' ?
13766 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13771 cv = function(v){ return v === true || v === "true" || v == 1; };
13778 if(v instanceof Date){
13782 if(dateFormat == "timestamp"){
13783 return new Date(v*1000);
13785 return Date.parseDate(v, dateFormat);
13787 var parsed = Date.parse(v);
13788 return parsed ? new Date(parsed) : null;
13797 Roo.data.Field.prototype = {
13805 * Ext JS Library 1.1.1
13806 * Copyright(c) 2006-2007, Ext JS, LLC.
13808 * Originally Released Under LGPL - original licence link has changed is not relivant.
13811 * <script type="text/javascript">
13814 // Base class for reading structured data from a data source. This class is intended to be
13815 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13818 * @class Roo.data.DataReader
13819 * Base class for reading structured data from a data source. This class is intended to be
13820 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13823 Roo.data.DataReader = function(meta, recordType){
13827 this.recordType = recordType instanceof Array ?
13828 Roo.data.Record.create(recordType) : recordType;
13831 Roo.data.DataReader.prototype = {
13834 readerType : 'Data',
13836 * Create an empty record
13837 * @param {Object} data (optional) - overlay some values
13838 * @return {Roo.data.Record} record created.
13840 newRow : function(d) {
13842 this.recordType.prototype.fields.each(function(c) {
13844 case 'int' : da[c.name] = 0; break;
13845 case 'date' : da[c.name] = new Date(); break;
13846 case 'float' : da[c.name] = 0.0; break;
13847 case 'boolean' : da[c.name] = false; break;
13848 default : da[c.name] = ""; break;
13852 return new this.recordType(Roo.apply(da, d));
13858 * Ext JS Library 1.1.1
13859 * Copyright(c) 2006-2007, Ext JS, LLC.
13861 * Originally Released Under LGPL - original licence link has changed is not relivant.
13864 * <script type="text/javascript">
13868 * @class Roo.data.DataProxy
13869 * @extends Roo.data.Observable
13870 * This class is an abstract base class for implementations which provide retrieval of
13871 * unformatted data objects.<br>
13873 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13874 * (of the appropriate type which knows how to parse the data object) to provide a block of
13875 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13877 * Custom implementations must implement the load method as described in
13878 * {@link Roo.data.HttpProxy#load}.
13880 Roo.data.DataProxy = function(){
13883 * @event beforeload
13884 * Fires before a network request is made to retrieve a data object.
13885 * @param {Object} This DataProxy object.
13886 * @param {Object} params The params parameter to the load function.
13891 * Fires before the load method's callback is called.
13892 * @param {Object} This DataProxy object.
13893 * @param {Object} o The data object.
13894 * @param {Object} arg The callback argument object passed to the load function.
13898 * @event loadexception
13899 * Fires if an Exception occurs during data retrieval.
13900 * @param {Object} This DataProxy object.
13901 * @param {Object} o The data object.
13902 * @param {Object} arg The callback argument object passed to the load function.
13903 * @param {Object} e The Exception.
13905 loadexception : true
13907 Roo.data.DataProxy.superclass.constructor.call(this);
13910 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13913 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13917 * Ext JS Library 1.1.1
13918 * Copyright(c) 2006-2007, Ext JS, LLC.
13920 * Originally Released Under LGPL - original licence link has changed is not relivant.
13923 * <script type="text/javascript">
13926 * @class Roo.data.MemoryProxy
13927 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13928 * to the Reader when its load method is called.
13930 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13932 Roo.data.MemoryProxy = function(data){
13936 Roo.data.MemoryProxy.superclass.constructor.call(this);
13940 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13943 * Load data from the requested source (in this case an in-memory
13944 * data object passed to the constructor), read the data object into
13945 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13946 * process that block using the passed callback.
13947 * @param {Object} params This parameter is not used by the MemoryProxy class.
13948 * @param {Roo.data.DataReader} reader The Reader object which converts the data
13949 * object into a block of Roo.data.Records.
13950 * @param {Function} callback The function into which to pass the block of Roo.data.records.
13951 * The function must be passed <ul>
13952 * <li>The Record block object</li>
13953 * <li>The "arg" argument from the load function</li>
13954 * <li>A boolean success indicator</li>
13956 * @param {Object} scope The scope in which to call the callback
13957 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13959 load : function(params, reader, callback, scope, arg){
13960 params = params || {};
13963 result = reader.readRecords(params.data ? params.data :this.data);
13965 this.fireEvent("loadexception", this, arg, null, e);
13966 callback.call(scope, null, arg, false);
13969 callback.call(scope, result, arg, true);
13973 update : function(params, records){
13978 * Ext JS Library 1.1.1
13979 * Copyright(c) 2006-2007, Ext JS, LLC.
13981 * Originally Released Under LGPL - original licence link has changed is not relivant.
13984 * <script type="text/javascript">
13987 * @class Roo.data.HttpProxy
13988 * @extends Roo.data.DataProxy
13989 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13990 * configured to reference a certain URL.<br><br>
13992 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13993 * from which the running page was served.<br><br>
13995 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13997 * Be aware that to enable the browser to parse an XML document, the server must set
13998 * the Content-Type header in the HTTP response to "text/xml".
14000 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14001 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14002 * will be used to make the request.
14004 Roo.data.HttpProxy = function(conn){
14005 Roo.data.HttpProxy.superclass.constructor.call(this);
14006 // is conn a conn config or a real conn?
14008 this.useAjax = !conn || !conn.events;
14012 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14013 // thse are take from connection...
14016 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14019 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14020 * extra parameters to each request made by this object. (defaults to undefined)
14023 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14024 * to each request made by this object. (defaults to undefined)
14027 * @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)
14030 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14033 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14039 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14043 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14044 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14045 * a finer-grained basis than the DataProxy events.
14047 getConnection : function(){
14048 return this.useAjax ? Roo.Ajax : this.conn;
14052 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14053 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14054 * process that block using the passed callback.
14055 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14056 * for the request to the remote server.
14057 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14058 * object into a block of Roo.data.Records.
14059 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14060 * The function must be passed <ul>
14061 * <li>The Record block object</li>
14062 * <li>The "arg" argument from the load function</li>
14063 * <li>A boolean success indicator</li>
14065 * @param {Object} scope The scope in which to call the callback
14066 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14068 load : function(params, reader, callback, scope, arg){
14069 if(this.fireEvent("beforeload", this, params) !== false){
14071 params : params || {},
14073 callback : callback,
14078 callback : this.loadResponse,
14082 Roo.applyIf(o, this.conn);
14083 if(this.activeRequest){
14084 Roo.Ajax.abort(this.activeRequest);
14086 this.activeRequest = Roo.Ajax.request(o);
14088 this.conn.request(o);
14091 callback.call(scope||this, null, arg, false);
14096 loadResponse : function(o, success, response){
14097 delete this.activeRequest;
14099 this.fireEvent("loadexception", this, o, response);
14100 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14105 result = o.reader.read(response);
14107 this.fireEvent("loadexception", this, o, response, e);
14108 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14112 this.fireEvent("load", this, o, o.request.arg);
14113 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14117 update : function(dataSet){
14122 updateResponse : function(dataSet){
14127 * Ext JS Library 1.1.1
14128 * Copyright(c) 2006-2007, Ext JS, LLC.
14130 * Originally Released Under LGPL - original licence link has changed is not relivant.
14133 * <script type="text/javascript">
14137 * @class Roo.data.ScriptTagProxy
14138 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14139 * other than the originating domain of the running page.<br><br>
14141 * <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
14142 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14144 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14145 * source code that is used as the source inside a <script> tag.<br><br>
14147 * In order for the browser to process the returned data, the server must wrap the data object
14148 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14149 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14150 * depending on whether the callback name was passed:
14153 boolean scriptTag = false;
14154 String cb = request.getParameter("callback");
14157 response.setContentType("text/javascript");
14159 response.setContentType("application/x-json");
14161 Writer out = response.getWriter();
14163 out.write(cb + "(");
14165 out.print(dataBlock.toJsonString());
14172 * @param {Object} config A configuration object.
14174 Roo.data.ScriptTagProxy = function(config){
14175 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14176 Roo.apply(this, config);
14177 this.head = document.getElementsByTagName("head")[0];
14180 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14182 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14184 * @cfg {String} url The URL from which to request the data object.
14187 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14191 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14192 * the server the name of the callback function set up by the load call to process the returned data object.
14193 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14194 * javascript output which calls this named function passing the data object as its only parameter.
14196 callbackParam : "callback",
14198 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14199 * name to the request.
14204 * Load data from the configured URL, read the data object into
14205 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14206 * process that block using the passed callback.
14207 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14208 * for the request to the remote server.
14209 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14210 * object into a block of Roo.data.Records.
14211 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14212 * The function must be passed <ul>
14213 * <li>The Record block object</li>
14214 * <li>The "arg" argument from the load function</li>
14215 * <li>A boolean success indicator</li>
14217 * @param {Object} scope The scope in which to call the callback
14218 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14220 load : function(params, reader, callback, scope, arg){
14221 if(this.fireEvent("beforeload", this, params) !== false){
14223 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14225 var url = this.url;
14226 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14228 url += "&_dc=" + (new Date().getTime());
14230 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14233 cb : "stcCallback"+transId,
14234 scriptId : "stcScript"+transId,
14238 callback : callback,
14244 window[trans.cb] = function(o){
14245 conn.handleResponse(o, trans);
14248 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14250 if(this.autoAbort !== false){
14254 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14256 var script = document.createElement("script");
14257 script.setAttribute("src", url);
14258 script.setAttribute("type", "text/javascript");
14259 script.setAttribute("id", trans.scriptId);
14260 this.head.appendChild(script);
14262 this.trans = trans;
14264 callback.call(scope||this, null, arg, false);
14269 isLoading : function(){
14270 return this.trans ? true : false;
14274 * Abort the current server request.
14276 abort : function(){
14277 if(this.isLoading()){
14278 this.destroyTrans(this.trans);
14283 destroyTrans : function(trans, isLoaded){
14284 this.head.removeChild(document.getElementById(trans.scriptId));
14285 clearTimeout(trans.timeoutId);
14287 window[trans.cb] = undefined;
14289 delete window[trans.cb];
14292 // if hasn't been loaded, wait for load to remove it to prevent script error
14293 window[trans.cb] = function(){
14294 window[trans.cb] = undefined;
14296 delete window[trans.cb];
14303 handleResponse : function(o, trans){
14304 this.trans = false;
14305 this.destroyTrans(trans, true);
14308 result = trans.reader.readRecords(o);
14310 this.fireEvent("loadexception", this, o, trans.arg, e);
14311 trans.callback.call(trans.scope||window, null, trans.arg, false);
14314 this.fireEvent("load", this, o, trans.arg);
14315 trans.callback.call(trans.scope||window, result, trans.arg, true);
14319 handleFailure : function(trans){
14320 this.trans = false;
14321 this.destroyTrans(trans, false);
14322 this.fireEvent("loadexception", this, null, trans.arg);
14323 trans.callback.call(trans.scope||window, null, trans.arg, false);
14327 * Ext JS Library 1.1.1
14328 * Copyright(c) 2006-2007, Ext JS, LLC.
14330 * Originally Released Under LGPL - original licence link has changed is not relivant.
14333 * <script type="text/javascript">
14337 * @class Roo.data.JsonReader
14338 * @extends Roo.data.DataReader
14339 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14340 * based on mappings in a provided Roo.data.Record constructor.
14342 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14343 * in the reply previously.
14348 var RecordDef = Roo.data.Record.create([
14349 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14350 {name: 'occupation'} // This field will use "occupation" as the mapping.
14352 var myReader = new Roo.data.JsonReader({
14353 totalProperty: "results", // The property which contains the total dataset size (optional)
14354 root: "rows", // The property which contains an Array of row objects
14355 id: "id" // The property within each row object that provides an ID for the record (optional)
14359 * This would consume a JSON file like this:
14361 { 'results': 2, 'rows': [
14362 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14363 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14366 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14367 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14368 * paged from the remote server.
14369 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14370 * @cfg {String} root name of the property which contains the Array of row objects.
14371 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14372 * @cfg {Array} fields Array of field definition objects
14374 * Create a new JsonReader
14375 * @param {Object} meta Metadata configuration options
14376 * @param {Object} recordType Either an Array of field definition objects,
14377 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14379 Roo.data.JsonReader = function(meta, recordType){
14382 // set some defaults:
14383 Roo.applyIf(meta, {
14384 totalProperty: 'total',
14385 successProperty : 'success',
14390 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14392 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14394 readerType : 'Json',
14397 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14398 * Used by Store query builder to append _requestMeta to params.
14401 metaFromRemote : false,
14403 * This method is only used by a DataProxy which has retrieved data from a remote server.
14404 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14405 * @return {Object} data A data block which is used by an Roo.data.Store object as
14406 * a cache of Roo.data.Records.
14408 read : function(response){
14409 var json = response.responseText;
14411 var o = /* eval:var:o */ eval("("+json+")");
14413 throw {message: "JsonReader.read: Json object not found"};
14419 this.metaFromRemote = true;
14420 this.meta = o.metaData;
14421 this.recordType = Roo.data.Record.create(o.metaData.fields);
14422 this.onMetaChange(this.meta, this.recordType, o);
14424 return this.readRecords(o);
14427 // private function a store will implement
14428 onMetaChange : function(meta, recordType, o){
14435 simpleAccess: function(obj, subsc) {
14442 getJsonAccessor: function(){
14444 return function(expr) {
14446 return(re.test(expr))
14447 ? new Function("obj", "return obj." + expr)
14452 return Roo.emptyFn;
14457 * Create a data block containing Roo.data.Records from an XML document.
14458 * @param {Object} o An object which contains an Array of row objects in the property specified
14459 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14460 * which contains the total size of the dataset.
14461 * @return {Object} data A data block which is used by an Roo.data.Store object as
14462 * a cache of Roo.data.Records.
14464 readRecords : function(o){
14466 * After any data loads, the raw JSON data is available for further custom processing.
14470 var s = this.meta, Record = this.recordType,
14471 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14473 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14475 if(s.totalProperty) {
14476 this.getTotal = this.getJsonAccessor(s.totalProperty);
14478 if(s.successProperty) {
14479 this.getSuccess = this.getJsonAccessor(s.successProperty);
14481 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14483 var g = this.getJsonAccessor(s.id);
14484 this.getId = function(rec) {
14486 return (r === undefined || r === "") ? null : r;
14489 this.getId = function(){return null;};
14492 for(var jj = 0; jj < fl; jj++){
14494 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14495 this.ef[jj] = this.getJsonAccessor(map);
14499 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14500 if(s.totalProperty){
14501 var vt = parseInt(this.getTotal(o), 10);
14506 if(s.successProperty){
14507 var vs = this.getSuccess(o);
14508 if(vs === false || vs === 'false'){
14513 for(var i = 0; i < c; i++){
14516 var id = this.getId(n);
14517 for(var j = 0; j < fl; j++){
14519 var v = this.ef[j](n);
14521 Roo.log('missing convert for ' + f.name);
14525 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14527 var record = new Record(values, id);
14529 records[i] = record;
14535 totalRecords : totalRecords
14538 // used when loading children.. @see loadDataFromChildren
14539 toLoadData: function(rec)
14541 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14542 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14543 return { data : data, total : data.length };
14548 * Ext JS Library 1.1.1
14549 * Copyright(c) 2006-2007, Ext JS, LLC.
14551 * Originally Released Under LGPL - original licence link has changed is not relivant.
14554 * <script type="text/javascript">
14558 * @class Roo.data.ArrayReader
14559 * @extends Roo.data.DataReader
14560 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14561 * Each element of that Array represents a row of data fields. The
14562 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14563 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14567 var RecordDef = Roo.data.Record.create([
14568 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14569 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14571 var myReader = new Roo.data.ArrayReader({
14572 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14576 * This would consume an Array like this:
14578 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14582 * Create a new JsonReader
14583 * @param {Object} meta Metadata configuration options.
14584 * @param {Object|Array} recordType Either an Array of field definition objects
14586 * @cfg {Array} fields Array of field definition objects
14587 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14588 * as specified to {@link Roo.data.Record#create},
14589 * or an {@link Roo.data.Record} object
14592 * created using {@link Roo.data.Record#create}.
14594 Roo.data.ArrayReader = function(meta, recordType)
14596 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14599 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14602 * Create a data block containing Roo.data.Records from an XML document.
14603 * @param {Object} o An Array of row objects which represents the dataset.
14604 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14605 * a cache of Roo.data.Records.
14607 readRecords : function(o)
14609 var sid = this.meta ? this.meta.id : null;
14610 var recordType = this.recordType, fields = recordType.prototype.fields;
14613 for(var i = 0; i < root.length; i++){
14616 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14617 for(var j = 0, jlen = fields.length; j < jlen; j++){
14618 var f = fields.items[j];
14619 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14620 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14622 values[f.name] = v;
14624 var record = new recordType(values, id);
14626 records[records.length] = record;
14630 totalRecords : records.length
14633 // used when loading children.. @see loadDataFromChildren
14634 toLoadData: function(rec)
14636 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14637 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14648 * @class Roo.bootstrap.ComboBox
14649 * @extends Roo.bootstrap.TriggerField
14650 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14651 * @cfg {Boolean} append (true|false) default false
14652 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14653 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14654 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14655 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14656 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14657 * @cfg {Boolean} animate default true
14658 * @cfg {Boolean} emptyResultText only for touch device
14659 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14660 * @cfg {String} emptyTitle default ''
14662 * Create a new ComboBox.
14663 * @param {Object} config Configuration options
14665 Roo.bootstrap.ComboBox = function(config){
14666 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14670 * Fires when the dropdown list is expanded
14671 * @param {Roo.bootstrap.ComboBox} combo This combo box
14676 * Fires when the dropdown list is collapsed
14677 * @param {Roo.bootstrap.ComboBox} combo This combo box
14681 * @event beforeselect
14682 * Fires before a list item is selected. Return false to cancel the selection.
14683 * @param {Roo.bootstrap.ComboBox} combo This combo box
14684 * @param {Roo.data.Record} record The data record returned from the underlying store
14685 * @param {Number} index The index of the selected item in the dropdown list
14687 'beforeselect' : true,
14690 * Fires when a list item is selected
14691 * @param {Roo.bootstrap.ComboBox} combo This combo box
14692 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14693 * @param {Number} index The index of the selected item in the dropdown list
14697 * @event beforequery
14698 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14699 * The event object passed has these properties:
14700 * @param {Roo.bootstrap.ComboBox} combo This combo box
14701 * @param {String} query The query
14702 * @param {Boolean} forceAll true to force "all" query
14703 * @param {Boolean} cancel true to cancel the query
14704 * @param {Object} e The query event object
14706 'beforequery': true,
14709 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14710 * @param {Roo.bootstrap.ComboBox} combo This combo box
14715 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14716 * @param {Roo.bootstrap.ComboBox} combo This combo box
14717 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14722 * Fires when the remove value from the combobox array
14723 * @param {Roo.bootstrap.ComboBox} combo This combo box
14727 * @event afterremove
14728 * Fires when the remove value from the combobox array
14729 * @param {Roo.bootstrap.ComboBox} combo This combo box
14731 'afterremove' : true,
14733 * @event specialfilter
14734 * Fires when specialfilter
14735 * @param {Roo.bootstrap.ComboBox} combo This combo box
14737 'specialfilter' : true,
14740 * Fires when tick the element
14741 * @param {Roo.bootstrap.ComboBox} combo This combo box
14745 * @event touchviewdisplay
14746 * Fires when touch view require special display (default is using displayField)
14747 * @param {Roo.bootstrap.ComboBox} combo This combo box
14748 * @param {Object} cfg set html .
14750 'touchviewdisplay' : true
14755 this.tickItems = [];
14757 this.selectedIndex = -1;
14758 if(this.mode == 'local'){
14759 if(config.queryDelay === undefined){
14760 this.queryDelay = 10;
14762 if(config.minChars === undefined){
14768 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14771 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14772 * rendering into an Roo.Editor, defaults to false)
14775 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14776 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14779 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14782 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14783 * the dropdown list (defaults to undefined, with no header element)
14787 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14791 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14793 listWidth: undefined,
14795 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14796 * mode = 'remote' or 'text' if mode = 'local')
14798 displayField: undefined,
14801 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14802 * mode = 'remote' or 'value' if mode = 'local').
14803 * Note: use of a valueField requires the user make a selection
14804 * in order for a value to be mapped.
14806 valueField: undefined,
14808 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14813 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14814 * field's data value (defaults to the underlying DOM element's name)
14816 hiddenName: undefined,
14818 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14822 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14824 selectedClass: 'active',
14827 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14831 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14832 * anchor positions (defaults to 'tl-bl')
14834 listAlign: 'tl-bl?',
14836 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14840 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14841 * query specified by the allQuery config option (defaults to 'query')
14843 triggerAction: 'query',
14845 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14846 * (defaults to 4, does not apply if editable = false)
14850 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14851 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14855 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14856 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14860 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14861 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14865 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14866 * when editable = true (defaults to false)
14868 selectOnFocus:false,
14870 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14872 queryParam: 'query',
14874 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14875 * when mode = 'remote' (defaults to 'Loading...')
14877 loadingText: 'Loading...',
14879 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14883 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14887 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14888 * traditional select (defaults to true)
14892 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14896 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14900 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14901 * listWidth has a higher value)
14905 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14906 * allow the user to set arbitrary text into the field (defaults to false)
14908 forceSelection:false,
14910 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14911 * if typeAhead = true (defaults to 250)
14913 typeAheadDelay : 250,
14915 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14916 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14918 valueNotFoundText : undefined,
14920 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14922 blockFocus : false,
14925 * @cfg {Boolean} disableClear Disable showing of clear button.
14927 disableClear : false,
14929 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
14931 alwaysQuery : false,
14934 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
14939 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14941 invalidClass : "has-warning",
14944 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14946 validClass : "has-success",
14949 * @cfg {Boolean} specialFilter (true|false) special filter default false
14951 specialFilter : false,
14954 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14956 mobileTouchView : true,
14959 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14961 useNativeIOS : false,
14964 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14966 mobile_restrict_height : false,
14968 ios_options : false,
14980 btnPosition : 'right',
14981 triggerList : true,
14982 showToggleBtn : true,
14984 emptyResultText: 'Empty',
14985 triggerText : 'Select',
14988 // element that contains real text value.. (when hidden is used..)
14990 getAutoCreate : function()
14995 * Render classic select for iso
14998 if(Roo.isIOS && this.useNativeIOS){
14999 cfg = this.getAutoCreateNativeIOS();
15007 if(Roo.isTouch && this.mobileTouchView){
15008 cfg = this.getAutoCreateTouchView();
15015 if(!this.tickable){
15016 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15021 * ComboBox with tickable selections
15024 var align = this.labelAlign || this.parentLabelAlign();
15027 cls : 'form-group roo-combobox-tickable' //input-group
15030 var btn_text_select = '';
15031 var btn_text_done = '';
15032 var btn_text_cancel = '';
15034 if (this.btn_text_show) {
15035 btn_text_select = 'Select';
15036 btn_text_done = 'Done';
15037 btn_text_cancel = 'Cancel';
15042 cls : 'tickable-buttons',
15047 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15048 //html : this.triggerText
15049 html: btn_text_select
15055 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15057 html: btn_text_done
15063 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15065 html: btn_text_cancel
15071 buttons.cn.unshift({
15073 cls: 'roo-select2-search-field-input'
15079 Roo.each(buttons.cn, function(c){
15081 c.cls += ' btn-' + _this.size;
15084 if (_this.disabled) {
15091 style : 'display: contents',
15096 cls: 'form-hidden-field'
15100 cls: 'roo-select2-choices',
15104 cls: 'roo-select2-search-field',
15115 cls: 'roo-select2-container input-group roo-select2-container-multi',
15121 // cls: 'typeahead typeahead-long dropdown-menu',
15122 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15127 if(this.hasFeedback && !this.allowBlank){
15131 cls: 'glyphicon form-control-feedback'
15134 combobox.cn.push(feedback);
15141 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15142 tooltip : 'This field is required'
15144 if (Roo.bootstrap.version == 4) {
15147 style : 'display:none'
15150 if (align ==='left' && this.fieldLabel.length) {
15152 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15159 cls : 'control-label col-form-label',
15160 html : this.fieldLabel
15172 var labelCfg = cfg.cn[1];
15173 var contentCfg = cfg.cn[2];
15176 if(this.indicatorpos == 'right'){
15182 cls : 'control-label col-form-label',
15186 html : this.fieldLabel
15202 labelCfg = cfg.cn[0];
15203 contentCfg = cfg.cn[1];
15207 if(this.labelWidth > 12){
15208 labelCfg.style = "width: " + this.labelWidth + 'px';
15211 if(this.labelWidth < 13 && this.labelmd == 0){
15212 this.labelmd = this.labelWidth;
15215 if(this.labellg > 0){
15216 labelCfg.cls += ' col-lg-' + this.labellg;
15217 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15220 if(this.labelmd > 0){
15221 labelCfg.cls += ' col-md-' + this.labelmd;
15222 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15225 if(this.labelsm > 0){
15226 labelCfg.cls += ' col-sm-' + this.labelsm;
15227 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15230 if(this.labelxs > 0){
15231 labelCfg.cls += ' col-xs-' + this.labelxs;
15232 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15236 } else if ( this.fieldLabel.length) {
15237 // Roo.log(" label");
15242 //cls : 'input-group-addon',
15243 html : this.fieldLabel
15248 if(this.indicatorpos == 'right'){
15252 //cls : 'input-group-addon',
15253 html : this.fieldLabel
15263 // Roo.log(" no label && no align");
15270 ['xs','sm','md','lg'].map(function(size){
15271 if (settings[size]) {
15272 cfg.cls += ' col-' + size + '-' + settings[size];
15280 _initEventsCalled : false,
15283 initEvents: function()
15285 if (this._initEventsCalled) { // as we call render... prevent looping...
15288 this._initEventsCalled = true;
15291 throw "can not find store for combo";
15294 this.indicator = this.indicatorEl();
15296 this.store = Roo.factory(this.store, Roo.data);
15297 this.store.parent = this;
15299 // if we are building from html. then this element is so complex, that we can not really
15300 // use the rendered HTML.
15301 // so we have to trash and replace the previous code.
15302 if (Roo.XComponent.build_from_html) {
15303 // remove this element....
15304 var e = this.el.dom, k=0;
15305 while (e ) { e = e.previousSibling; ++k;}
15310 this.rendered = false;
15312 this.render(this.parent().getChildContainer(true), k);
15315 if(Roo.isIOS && this.useNativeIOS){
15316 this.initIOSView();
15324 if(Roo.isTouch && this.mobileTouchView){
15325 this.initTouchView();
15330 this.initTickableEvents();
15334 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15336 if(this.hiddenName){
15338 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15340 this.hiddenField.dom.value =
15341 this.hiddenValue !== undefined ? this.hiddenValue :
15342 this.value !== undefined ? this.value : '';
15344 // prevent input submission
15345 this.el.dom.removeAttribute('name');
15346 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15351 // this.el.dom.setAttribute('autocomplete', 'off');
15354 var cls = 'x-combo-list';
15356 //this.list = new Roo.Layer({
15357 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15363 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15364 _this.list.setWidth(lw);
15367 this.list.on('mouseover', this.onViewOver, this);
15368 this.list.on('mousemove', this.onViewMove, this);
15369 this.list.on('scroll', this.onViewScroll, this);
15372 this.list.swallowEvent('mousewheel');
15373 this.assetHeight = 0;
15376 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15377 this.assetHeight += this.header.getHeight();
15380 this.innerList = this.list.createChild({cls:cls+'-inner'});
15381 this.innerList.on('mouseover', this.onViewOver, this);
15382 this.innerList.on('mousemove', this.onViewMove, this);
15383 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15385 if(this.allowBlank && !this.pageSize && !this.disableClear){
15386 this.footer = this.list.createChild({cls:cls+'-ft'});
15387 this.pageTb = new Roo.Toolbar(this.footer);
15391 this.footer = this.list.createChild({cls:cls+'-ft'});
15392 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15393 {pageSize: this.pageSize});
15397 if (this.pageTb && this.allowBlank && !this.disableClear) {
15399 this.pageTb.add(new Roo.Toolbar.Fill(), {
15400 cls: 'x-btn-icon x-btn-clear',
15402 handler: function()
15405 _this.clearValue();
15406 _this.onSelect(false, -1);
15411 this.assetHeight += this.footer.getHeight();
15416 this.tpl = Roo.bootstrap.version == 4 ?
15417 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15418 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15421 this.view = new Roo.View(this.list, this.tpl, {
15422 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15424 //this.view.wrapEl.setDisplayed(false);
15425 this.view.on('click', this.onViewClick, this);
15428 this.store.on('beforeload', this.onBeforeLoad, this);
15429 this.store.on('load', this.onLoad, this);
15430 this.store.on('loadexception', this.onLoadException, this);
15432 if(this.resizable){
15433 this.resizer = new Roo.Resizable(this.list, {
15434 pinned:true, handles:'se'
15436 this.resizer.on('resize', function(r, w, h){
15437 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15438 this.listWidth = w;
15439 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15440 this.restrictHeight();
15442 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15445 if(!this.editable){
15446 this.editable = true;
15447 this.setEditable(false);
15452 if (typeof(this.events.add.listeners) != 'undefined') {
15454 this.addicon = this.wrap.createChild(
15455 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15457 this.addicon.on('click', function(e) {
15458 this.fireEvent('add', this);
15461 if (typeof(this.events.edit.listeners) != 'undefined') {
15463 this.editicon = this.wrap.createChild(
15464 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15465 if (this.addicon) {
15466 this.editicon.setStyle('margin-left', '40px');
15468 this.editicon.on('click', function(e) {
15470 // we fire even if inothing is selected..
15471 this.fireEvent('edit', this, this.lastData );
15477 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15478 "up" : function(e){
15479 this.inKeyMode = true;
15483 "down" : function(e){
15484 if(!this.isExpanded()){
15485 this.onTriggerClick();
15487 this.inKeyMode = true;
15492 "enter" : function(e){
15493 // this.onViewClick();
15497 if(this.fireEvent("specialkey", this, e)){
15498 this.onViewClick(false);
15504 "esc" : function(e){
15508 "tab" : function(e){
15511 if(this.fireEvent("specialkey", this, e)){
15512 this.onViewClick(false);
15520 doRelay : function(foo, bar, hname){
15521 if(hname == 'down' || this.scope.isExpanded()){
15522 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15531 this.queryDelay = Math.max(this.queryDelay || 10,
15532 this.mode == 'local' ? 10 : 250);
15535 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15537 if(this.typeAhead){
15538 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15540 if(this.editable !== false){
15541 this.inputEl().on("keyup", this.onKeyUp, this);
15543 if(this.forceSelection){
15544 this.inputEl().on('blur', this.doForce, this);
15548 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15549 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15553 initTickableEvents: function()
15557 if(this.hiddenName){
15559 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15561 this.hiddenField.dom.value =
15562 this.hiddenValue !== undefined ? this.hiddenValue :
15563 this.value !== undefined ? this.value : '';
15565 // prevent input submission
15566 this.el.dom.removeAttribute('name');
15567 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15572 // this.list = this.el.select('ul.dropdown-menu',true).first();
15574 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15575 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15576 if(this.triggerList){
15577 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15580 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15581 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15583 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15584 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15586 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15587 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15589 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15590 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15591 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15594 this.cancelBtn.hide();
15599 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15600 _this.list.setWidth(lw);
15603 this.list.on('mouseover', this.onViewOver, this);
15604 this.list.on('mousemove', this.onViewMove, this);
15606 this.list.on('scroll', this.onViewScroll, this);
15609 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15610 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15613 this.view = new Roo.View(this.list, this.tpl, {
15618 selectedClass: this.selectedClass
15621 //this.view.wrapEl.setDisplayed(false);
15622 this.view.on('click', this.onViewClick, this);
15626 this.store.on('beforeload', this.onBeforeLoad, this);
15627 this.store.on('load', this.onLoad, this);
15628 this.store.on('loadexception', this.onLoadException, this);
15631 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15632 "up" : function(e){
15633 this.inKeyMode = true;
15637 "down" : function(e){
15638 this.inKeyMode = true;
15642 "enter" : function(e){
15643 if(this.fireEvent("specialkey", this, e)){
15644 this.onViewClick(false);
15650 "esc" : function(e){
15651 this.onTickableFooterButtonClick(e, false, false);
15654 "tab" : function(e){
15655 this.fireEvent("specialkey", this, e);
15657 this.onTickableFooterButtonClick(e, false, false);
15664 doRelay : function(e, fn, key){
15665 if(this.scope.isExpanded()){
15666 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15675 this.queryDelay = Math.max(this.queryDelay || 10,
15676 this.mode == 'local' ? 10 : 250);
15679 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15681 if(this.typeAhead){
15682 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15685 if(this.editable !== false){
15686 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15689 this.indicator = this.indicatorEl();
15691 if(this.indicator){
15692 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15693 this.indicator.hide();
15698 onDestroy : function(){
15700 this.view.setStore(null);
15701 this.view.el.removeAllListeners();
15702 this.view.el.remove();
15703 this.view.purgeListeners();
15706 this.list.dom.innerHTML = '';
15710 this.store.un('beforeload', this.onBeforeLoad, this);
15711 this.store.un('load', this.onLoad, this);
15712 this.store.un('loadexception', this.onLoadException, this);
15714 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15718 fireKey : function(e){
15719 if(e.isNavKeyPress() && !this.list.isVisible()){
15720 this.fireEvent("specialkey", this, e);
15725 onResize: function(w, h){
15726 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15728 // if(typeof w != 'number'){
15729 // // we do not handle it!?!?
15732 // var tw = this.trigger.getWidth();
15733 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15734 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15736 // this.inputEl().setWidth( this.adjustWidth('input', x));
15738 // //this.trigger.setStyle('left', x+'px');
15740 // if(this.list && this.listWidth === undefined){
15741 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15742 // this.list.setWidth(lw);
15743 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15751 * Allow or prevent the user from directly editing the field text. If false is passed,
15752 * the user will only be able to select from the items defined in the dropdown list. This method
15753 * is the runtime equivalent of setting the 'editable' config option at config time.
15754 * @param {Boolean} value True to allow the user to directly edit the field text
15756 setEditable : function(value){
15757 if(value == this.editable){
15760 this.editable = value;
15762 this.inputEl().dom.setAttribute('readOnly', true);
15763 this.inputEl().on('mousedown', this.onTriggerClick, this);
15764 this.inputEl().addClass('x-combo-noedit');
15766 this.inputEl().dom.setAttribute('readOnly', false);
15767 this.inputEl().un('mousedown', this.onTriggerClick, this);
15768 this.inputEl().removeClass('x-combo-noedit');
15774 onBeforeLoad : function(combo,opts){
15775 if(!this.hasFocus){
15779 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15781 this.restrictHeight();
15782 this.selectedIndex = -1;
15786 onLoad : function(){
15788 this.hasQuery = false;
15790 if(!this.hasFocus){
15794 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15795 this.loading.hide();
15798 if(this.store.getCount() > 0){
15801 this.restrictHeight();
15802 if(this.lastQuery == this.allQuery){
15803 if(this.editable && !this.tickable){
15804 this.inputEl().dom.select();
15808 !this.selectByValue(this.value, true) &&
15811 !this.store.lastOptions ||
15812 typeof(this.store.lastOptions.add) == 'undefined' ||
15813 this.store.lastOptions.add != true
15816 this.select(0, true);
15819 if(this.autoFocus){
15822 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15823 this.taTask.delay(this.typeAheadDelay);
15827 this.onEmptyResults();
15833 onLoadException : function()
15835 this.hasQuery = false;
15837 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15838 this.loading.hide();
15841 if(this.tickable && this.editable){
15846 // only causes errors at present
15847 //Roo.log(this.store.reader.jsonData);
15848 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15850 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15856 onTypeAhead : function(){
15857 if(this.store.getCount() > 0){
15858 var r = this.store.getAt(0);
15859 var newValue = r.data[this.displayField];
15860 var len = newValue.length;
15861 var selStart = this.getRawValue().length;
15863 if(selStart != len){
15864 this.setRawValue(newValue);
15865 this.selectText(selStart, newValue.length);
15871 onSelect : function(record, index){
15873 if(this.fireEvent('beforeselect', this, record, index) !== false){
15875 this.setFromData(index > -1 ? record.data : false);
15878 this.fireEvent('select', this, record, index);
15883 * Returns the currently selected field value or empty string if no value is set.
15884 * @return {String} value The selected value
15886 getValue : function()
15888 if(Roo.isIOS && this.useNativeIOS){
15889 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15893 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15896 if(this.valueField){
15897 return typeof this.value != 'undefined' ? this.value : '';
15899 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15903 getRawValue : function()
15905 if(Roo.isIOS && this.useNativeIOS){
15906 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15909 var v = this.inputEl().getValue();
15915 * Clears any text/value currently set in the field
15917 clearValue : function(){
15919 if(this.hiddenField){
15920 this.hiddenField.dom.value = '';
15923 this.setRawValue('');
15924 this.lastSelectionText = '';
15925 this.lastData = false;
15927 var close = this.closeTriggerEl();
15938 * Sets the specified value into the field. If the value finds a match, the corresponding record text
15939 * will be displayed in the field. If the value does not match the data value of an existing item,
15940 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15941 * Otherwise the field will be blank (although the value will still be set).
15942 * @param {String} value The value to match
15944 setValue : function(v)
15946 if(Roo.isIOS && this.useNativeIOS){
15947 this.setIOSValue(v);
15957 if(this.valueField){
15958 var r = this.findRecord(this.valueField, v);
15960 text = r.data[this.displayField];
15961 }else if(this.valueNotFoundText !== undefined){
15962 text = this.valueNotFoundText;
15965 this.lastSelectionText = text;
15966 if(this.hiddenField){
15967 this.hiddenField.dom.value = v;
15969 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15972 var close = this.closeTriggerEl();
15975 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15981 * @property {Object} the last set data for the element
15986 * Sets the value of the field based on a object which is related to the record format for the store.
15987 * @param {Object} value the value to set as. or false on reset?
15989 setFromData : function(o){
15996 var dv = ''; // display value
15997 var vv = ''; // value value..
15999 if (this.displayField) {
16000 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16002 // this is an error condition!!!
16003 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16006 if(this.valueField){
16007 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16010 var close = this.closeTriggerEl();
16013 if(dv.length || vv * 1 > 0){
16015 this.blockFocus=true;
16021 if(this.hiddenField){
16022 this.hiddenField.dom.value = vv;
16024 this.lastSelectionText = dv;
16025 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16029 // no hidden field.. - we store the value in 'value', but still display
16030 // display field!!!!
16031 this.lastSelectionText = dv;
16032 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16039 reset : function(){
16040 // overridden so that last data is reset..
16047 this.setValue(this.originalValue);
16048 //this.clearInvalid();
16049 this.lastData = false;
16051 this.view.clearSelections();
16057 findRecord : function(prop, value){
16059 if(this.store.getCount() > 0){
16060 this.store.each(function(r){
16061 if(r.data[prop] == value){
16071 getName: function()
16073 // returns hidden if it's set..
16074 if (!this.rendered) {return ''};
16075 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16079 onViewMove : function(e, t){
16080 this.inKeyMode = false;
16084 onViewOver : function(e, t){
16085 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16088 var item = this.view.findItemFromChild(t);
16091 var index = this.view.indexOf(item);
16092 this.select(index, false);
16097 onViewClick : function(view, doFocus, el, e)
16099 var index = this.view.getSelectedIndexes()[0];
16101 var r = this.store.getAt(index);
16105 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16112 Roo.each(this.tickItems, function(v,k){
16114 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16116 _this.tickItems.splice(k, 1);
16118 if(typeof(e) == 'undefined' && view == false){
16119 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16131 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16132 this.tickItems.push(r.data);
16135 if(typeof(e) == 'undefined' && view == false){
16136 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16143 this.onSelect(r, index);
16145 if(doFocus !== false && !this.blockFocus){
16146 this.inputEl().focus();
16151 restrictHeight : function(){
16152 //this.innerList.dom.style.height = '';
16153 //var inner = this.innerList.dom;
16154 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16155 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16156 //this.list.beginUpdate();
16157 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16158 this.list.alignTo(this.inputEl(), this.listAlign);
16159 this.list.alignTo(this.inputEl(), this.listAlign);
16160 //this.list.endUpdate();
16164 onEmptyResults : function(){
16166 if(this.tickable && this.editable){
16167 this.hasFocus = false;
16168 this.restrictHeight();
16176 * Returns true if the dropdown list is expanded, else false.
16178 isExpanded : function(){
16179 return this.list.isVisible();
16183 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16184 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16185 * @param {String} value The data value of the item to select
16186 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16187 * selected item if it is not currently in view (defaults to true)
16188 * @return {Boolean} True if the value matched an item in the list, else false
16190 selectByValue : function(v, scrollIntoView){
16191 if(v !== undefined && v !== null){
16192 var r = this.findRecord(this.valueField || this.displayField, v);
16194 this.select(this.store.indexOf(r), scrollIntoView);
16202 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16203 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16204 * @param {Number} index The zero-based index of the list item to select
16205 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16206 * selected item if it is not currently in view (defaults to true)
16208 select : function(index, scrollIntoView){
16209 this.selectedIndex = index;
16210 this.view.select(index);
16211 if(scrollIntoView !== false){
16212 var el = this.view.getNode(index);
16214 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16217 this.list.scrollChildIntoView(el, false);
16223 selectNext : function(){
16224 var ct = this.store.getCount();
16226 if(this.selectedIndex == -1){
16228 }else if(this.selectedIndex < ct-1){
16229 this.select(this.selectedIndex+1);
16235 selectPrev : function(){
16236 var ct = this.store.getCount();
16238 if(this.selectedIndex == -1){
16240 }else if(this.selectedIndex != 0){
16241 this.select(this.selectedIndex-1);
16247 onKeyUp : function(e){
16248 if(this.editable !== false && !e.isSpecialKey()){
16249 this.lastKey = e.getKey();
16250 this.dqTask.delay(this.queryDelay);
16255 validateBlur : function(){
16256 return !this.list || !this.list.isVisible();
16260 initQuery : function(){
16262 var v = this.getRawValue();
16264 if(this.tickable && this.editable){
16265 v = this.tickableInputEl().getValue();
16272 doForce : function(){
16273 if(this.inputEl().dom.value.length > 0){
16274 this.inputEl().dom.value =
16275 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16281 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16282 * query allowing the query action to be canceled if needed.
16283 * @param {String} query The SQL query to execute
16284 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16285 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16286 * saved in the current store (defaults to false)
16288 doQuery : function(q, forceAll){
16290 if(q === undefined || q === null){
16295 forceAll: forceAll,
16299 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16304 forceAll = qe.forceAll;
16305 if(forceAll === true || (q.length >= this.minChars)){
16307 this.hasQuery = true;
16309 if(this.lastQuery != q || this.alwaysQuery){
16310 this.lastQuery = q;
16311 if(this.mode == 'local'){
16312 this.selectedIndex = -1;
16314 this.store.clearFilter();
16317 if(this.specialFilter){
16318 this.fireEvent('specialfilter', this);
16323 this.store.filter(this.displayField, q);
16326 this.store.fireEvent("datachanged", this.store);
16333 this.store.baseParams[this.queryParam] = q;
16335 var options = {params : this.getParams(q)};
16338 options.add = true;
16339 options.params.start = this.page * this.pageSize;
16342 this.store.load(options);
16345 * this code will make the page width larger, at the beginning, the list not align correctly,
16346 * we should expand the list on onLoad
16347 * so command out it
16352 this.selectedIndex = -1;
16357 this.loadNext = false;
16361 getParams : function(q){
16363 //p[this.queryParam] = q;
16367 p.limit = this.pageSize;
16373 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16375 collapse : function(){
16376 if(!this.isExpanded()){
16382 this.hasFocus = false;
16386 this.cancelBtn.hide();
16387 this.trigger.show();
16390 this.tickableInputEl().dom.value = '';
16391 this.tickableInputEl().blur();
16396 Roo.get(document).un('mousedown', this.collapseIf, this);
16397 Roo.get(document).un('mousewheel', this.collapseIf, this);
16398 if (!this.editable) {
16399 Roo.get(document).un('keydown', this.listKeyPress, this);
16401 this.fireEvent('collapse', this);
16407 collapseIf : function(e){
16408 var in_combo = e.within(this.el);
16409 var in_list = e.within(this.list);
16410 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16412 if (in_combo || in_list || is_list) {
16413 //e.stopPropagation();
16418 this.onTickableFooterButtonClick(e, false, false);
16426 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16428 expand : function(){
16430 if(this.isExpanded() || !this.hasFocus){
16434 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16435 this.list.setWidth(lw);
16441 this.restrictHeight();
16445 this.tickItems = Roo.apply([], this.item);
16448 this.cancelBtn.show();
16449 this.trigger.hide();
16452 this.tickableInputEl().focus();
16457 Roo.get(document).on('mousedown', this.collapseIf, this);
16458 Roo.get(document).on('mousewheel', this.collapseIf, this);
16459 if (!this.editable) {
16460 Roo.get(document).on('keydown', this.listKeyPress, this);
16463 this.fireEvent('expand', this);
16467 // Implements the default empty TriggerField.onTriggerClick function
16468 onTriggerClick : function(e)
16470 Roo.log('trigger click');
16472 if(this.disabled || !this.triggerList){
16477 this.loadNext = false;
16479 if(this.isExpanded()){
16481 if (!this.blockFocus) {
16482 this.inputEl().focus();
16486 this.hasFocus = true;
16487 if(this.triggerAction == 'all') {
16488 this.doQuery(this.allQuery, true);
16490 this.doQuery(this.getRawValue());
16492 if (!this.blockFocus) {
16493 this.inputEl().focus();
16498 onTickableTriggerClick : function(e)
16505 this.loadNext = false;
16506 this.hasFocus = true;
16508 if(this.triggerAction == 'all') {
16509 this.doQuery(this.allQuery, true);
16511 this.doQuery(this.getRawValue());
16515 onSearchFieldClick : function(e)
16517 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16518 this.onTickableFooterButtonClick(e, false, false);
16522 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16527 this.loadNext = false;
16528 this.hasFocus = true;
16530 if(this.triggerAction == 'all') {
16531 this.doQuery(this.allQuery, true);
16533 this.doQuery(this.getRawValue());
16537 listKeyPress : function(e)
16539 //Roo.log('listkeypress');
16540 // scroll to first matching element based on key pres..
16541 if (e.isSpecialKey()) {
16544 var k = String.fromCharCode(e.getKey()).toUpperCase();
16547 var csel = this.view.getSelectedNodes();
16548 var cselitem = false;
16550 var ix = this.view.indexOf(csel[0]);
16551 cselitem = this.store.getAt(ix);
16552 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16558 this.store.each(function(v) {
16560 // start at existing selection.
16561 if (cselitem.id == v.id) {
16567 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16568 match = this.store.indexOf(v);
16574 if (match === false) {
16575 return true; // no more action?
16578 this.view.select(match);
16579 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16580 sn.scrollIntoView(sn.dom.parentNode, false);
16583 onViewScroll : function(e, t){
16585 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){
16589 this.hasQuery = true;
16591 this.loading = this.list.select('.loading', true).first();
16593 if(this.loading === null){
16594 this.list.createChild({
16596 cls: 'loading roo-select2-more-results roo-select2-active',
16597 html: 'Loading more results...'
16600 this.loading = this.list.select('.loading', true).first();
16602 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16604 this.loading.hide();
16607 this.loading.show();
16612 this.loadNext = true;
16614 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16619 addItem : function(o)
16621 var dv = ''; // display value
16623 if (this.displayField) {
16624 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16626 // this is an error condition!!!
16627 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16634 var choice = this.choices.createChild({
16636 cls: 'roo-select2-search-choice',
16645 cls: 'roo-select2-search-choice-close fa fa-times',
16650 }, this.searchField);
16652 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16654 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16662 this.inputEl().dom.value = '';
16667 onRemoveItem : function(e, _self, o)
16669 e.preventDefault();
16671 this.lastItem = Roo.apply([], this.item);
16673 var index = this.item.indexOf(o.data) * 1;
16676 Roo.log('not this item?!');
16680 this.item.splice(index, 1);
16685 this.fireEvent('remove', this, e);
16691 syncValue : function()
16693 if(!this.item.length){
16700 Roo.each(this.item, function(i){
16701 if(_this.valueField){
16702 value.push(i[_this.valueField]);
16709 this.value = value.join(',');
16711 if(this.hiddenField){
16712 this.hiddenField.dom.value = this.value;
16715 this.store.fireEvent("datachanged", this.store);
16720 clearItem : function()
16722 if(!this.multiple){
16728 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16736 if(this.tickable && !Roo.isTouch){
16737 this.view.refresh();
16741 inputEl: function ()
16743 if(Roo.isIOS && this.useNativeIOS){
16744 return this.el.select('select.roo-ios-select', true).first();
16747 if(Roo.isTouch && this.mobileTouchView){
16748 return this.el.select('input.form-control',true).first();
16752 return this.searchField;
16755 return this.el.select('input.form-control',true).first();
16758 onTickableFooterButtonClick : function(e, btn, el)
16760 e.preventDefault();
16762 this.lastItem = Roo.apply([], this.item);
16764 if(btn && btn.name == 'cancel'){
16765 this.tickItems = Roo.apply([], this.item);
16774 Roo.each(this.tickItems, function(o){
16782 validate : function()
16784 if(this.getVisibilityEl().hasClass('hidden')){
16788 var v = this.getRawValue();
16791 v = this.getValue();
16794 if(this.disabled || this.allowBlank || v.length){
16799 this.markInvalid();
16803 tickableInputEl : function()
16805 if(!this.tickable || !this.editable){
16806 return this.inputEl();
16809 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16813 getAutoCreateTouchView : function()
16818 cls: 'form-group' //input-group
16824 type : this.inputType,
16825 cls : 'form-control x-combo-noedit',
16826 autocomplete: 'new-password',
16827 placeholder : this.placeholder || '',
16832 input.name = this.name;
16836 input.cls += ' input-' + this.size;
16839 if (this.disabled) {
16840 input.disabled = true;
16851 inputblock.cls += ' input-group';
16853 inputblock.cn.unshift({
16855 cls : 'input-group-addon input-group-prepend input-group-text',
16860 if(this.removable && !this.multiple){
16861 inputblock.cls += ' roo-removable';
16863 inputblock.cn.push({
16866 cls : 'roo-combo-removable-btn close'
16870 if(this.hasFeedback && !this.allowBlank){
16872 inputblock.cls += ' has-feedback';
16874 inputblock.cn.push({
16876 cls: 'glyphicon form-control-feedback'
16883 inputblock.cls += (this.before) ? '' : ' input-group';
16885 inputblock.cn.push({
16887 cls : 'input-group-addon input-group-append input-group-text',
16893 var ibwrap = inputblock;
16898 cls: 'roo-select2-choices',
16902 cls: 'roo-select2-search-field',
16915 cls: 'roo-select2-container input-group roo-touchview-combobox ',
16920 cls: 'form-hidden-field'
16926 if(!this.multiple && this.showToggleBtn){
16932 if (this.caret != false) {
16935 cls: 'fa fa-' + this.caret
16942 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16944 Roo.bootstrap.version == 3 ? caret : '',
16947 cls: 'combobox-clear',
16961 combobox.cls += ' roo-select2-container-multi';
16964 var align = this.labelAlign || this.parentLabelAlign();
16966 if (align ==='left' && this.fieldLabel.length) {
16971 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16972 tooltip : 'This field is required'
16976 cls : 'control-label col-form-label',
16977 html : this.fieldLabel
16988 var labelCfg = cfg.cn[1];
16989 var contentCfg = cfg.cn[2];
16992 if(this.indicatorpos == 'right'){
16997 cls : 'control-label col-form-label',
17001 html : this.fieldLabel
17005 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17006 tooltip : 'This field is required'
17019 labelCfg = cfg.cn[0];
17020 contentCfg = cfg.cn[1];
17025 if(this.labelWidth > 12){
17026 labelCfg.style = "width: " + this.labelWidth + 'px';
17029 if(this.labelWidth < 13 && this.labelmd == 0){
17030 this.labelmd = this.labelWidth;
17033 if(this.labellg > 0){
17034 labelCfg.cls += ' col-lg-' + this.labellg;
17035 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17038 if(this.labelmd > 0){
17039 labelCfg.cls += ' col-md-' + this.labelmd;
17040 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17043 if(this.labelsm > 0){
17044 labelCfg.cls += ' col-sm-' + this.labelsm;
17045 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17048 if(this.labelxs > 0){
17049 labelCfg.cls += ' col-xs-' + this.labelxs;
17050 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17054 } else if ( this.fieldLabel.length) {
17058 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17059 tooltip : 'This field is required'
17063 cls : 'control-label',
17064 html : this.fieldLabel
17075 if(this.indicatorpos == 'right'){
17079 cls : 'control-label',
17080 html : this.fieldLabel,
17084 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17085 tooltip : 'This field is required'
17102 var settings = this;
17104 ['xs','sm','md','lg'].map(function(size){
17105 if (settings[size]) {
17106 cfg.cls += ' col-' + size + '-' + settings[size];
17113 initTouchView : function()
17115 this.renderTouchView();
17117 this.touchViewEl.on('scroll', function(){
17118 this.el.dom.scrollTop = 0;
17121 this.originalValue = this.getValue();
17123 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17125 this.inputEl().on("click", this.showTouchView, this);
17126 if (this.triggerEl) {
17127 this.triggerEl.on("click", this.showTouchView, this);
17131 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17132 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17134 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17136 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17137 this.store.on('load', this.onTouchViewLoad, this);
17138 this.store.on('loadexception', this.onTouchViewLoadException, this);
17140 if(this.hiddenName){
17142 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17144 this.hiddenField.dom.value =
17145 this.hiddenValue !== undefined ? this.hiddenValue :
17146 this.value !== undefined ? this.value : '';
17148 this.el.dom.removeAttribute('name');
17149 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17153 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17154 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17157 if(this.removable && !this.multiple){
17158 var close = this.closeTriggerEl();
17160 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17161 close.on('click', this.removeBtnClick, this, close);
17165 * fix the bug in Safari iOS8
17167 this.inputEl().on("focus", function(e){
17168 document.activeElement.blur();
17171 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17178 renderTouchView : function()
17180 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17181 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17183 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17184 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17186 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17187 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17188 this.touchViewBodyEl.setStyle('overflow', 'auto');
17190 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17191 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17193 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17194 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17198 showTouchView : function()
17204 this.touchViewHeaderEl.hide();
17206 if(this.modalTitle.length){
17207 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17208 this.touchViewHeaderEl.show();
17211 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17212 this.touchViewEl.show();
17214 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17216 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17217 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17219 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17221 if(this.modalTitle.length){
17222 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17225 this.touchViewBodyEl.setHeight(bodyHeight);
17229 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17231 this.touchViewEl.addClass('in');
17234 if(this._touchViewMask){
17235 Roo.get(document.body).addClass("x-body-masked");
17236 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17237 this._touchViewMask.setStyle('z-index', 10000);
17238 this._touchViewMask.addClass('show');
17241 this.doTouchViewQuery();
17245 hideTouchView : function()
17247 this.touchViewEl.removeClass('in');
17251 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17253 this.touchViewEl.setStyle('display', 'none');
17256 if(this._touchViewMask){
17257 this._touchViewMask.removeClass('show');
17258 Roo.get(document.body).removeClass("x-body-masked");
17262 setTouchViewValue : function()
17269 Roo.each(this.tickItems, function(o){
17274 this.hideTouchView();
17277 doTouchViewQuery : function()
17286 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17290 if(!this.alwaysQuery || this.mode == 'local'){
17291 this.onTouchViewLoad();
17298 onTouchViewBeforeLoad : function(combo,opts)
17304 onTouchViewLoad : function()
17306 if(this.store.getCount() < 1){
17307 this.onTouchViewEmptyResults();
17311 this.clearTouchView();
17313 var rawValue = this.getRawValue();
17315 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17317 this.tickItems = [];
17319 this.store.data.each(function(d, rowIndex){
17320 var row = this.touchViewListGroup.createChild(template);
17322 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17323 row.addClass(d.data.cls);
17326 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17329 html : d.data[this.displayField]
17332 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17333 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17336 row.removeClass('selected');
17337 if(!this.multiple && this.valueField &&
17338 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17341 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17342 row.addClass('selected');
17345 if(this.multiple && this.valueField &&
17346 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17350 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17351 this.tickItems.push(d.data);
17354 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17358 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17360 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17362 if(this.modalTitle.length){
17363 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17366 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17368 if(this.mobile_restrict_height && listHeight < bodyHeight){
17369 this.touchViewBodyEl.setHeight(listHeight);
17374 if(firstChecked && listHeight > bodyHeight){
17375 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17380 onTouchViewLoadException : function()
17382 this.hideTouchView();
17385 onTouchViewEmptyResults : function()
17387 this.clearTouchView();
17389 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17391 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17395 clearTouchView : function()
17397 this.touchViewListGroup.dom.innerHTML = '';
17400 onTouchViewClick : function(e, el, o)
17402 e.preventDefault();
17405 var rowIndex = o.rowIndex;
17407 var r = this.store.getAt(rowIndex);
17409 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17411 if(!this.multiple){
17412 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17413 c.dom.removeAttribute('checked');
17416 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17418 this.setFromData(r.data);
17420 var close = this.closeTriggerEl();
17426 this.hideTouchView();
17428 this.fireEvent('select', this, r, rowIndex);
17433 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17434 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17435 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17439 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17440 this.addItem(r.data);
17441 this.tickItems.push(r.data);
17445 getAutoCreateNativeIOS : function()
17448 cls: 'form-group' //input-group,
17453 cls : 'roo-ios-select'
17457 combobox.name = this.name;
17460 if (this.disabled) {
17461 combobox.disabled = true;
17464 var settings = this;
17466 ['xs','sm','md','lg'].map(function(size){
17467 if (settings[size]) {
17468 cfg.cls += ' col-' + size + '-' + settings[size];
17478 initIOSView : function()
17480 this.store.on('load', this.onIOSViewLoad, this);
17485 onIOSViewLoad : function()
17487 if(this.store.getCount() < 1){
17491 this.clearIOSView();
17493 if(this.allowBlank) {
17495 var default_text = '-- SELECT --';
17497 if(this.placeholder.length){
17498 default_text = this.placeholder;
17501 if(this.emptyTitle.length){
17502 default_text += ' - ' + this.emptyTitle + ' -';
17505 var opt = this.inputEl().createChild({
17508 html : default_text
17512 o[this.valueField] = 0;
17513 o[this.displayField] = default_text;
17515 this.ios_options.push({
17522 this.store.data.each(function(d, rowIndex){
17526 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17527 html = d.data[this.displayField];
17532 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17533 value = d.data[this.valueField];
17542 if(this.value == d.data[this.valueField]){
17543 option['selected'] = true;
17546 var opt = this.inputEl().createChild(option);
17548 this.ios_options.push({
17555 this.inputEl().on('change', function(){
17556 this.fireEvent('select', this);
17561 clearIOSView: function()
17563 this.inputEl().dom.innerHTML = '';
17565 this.ios_options = [];
17568 setIOSValue: function(v)
17572 if(!this.ios_options){
17576 Roo.each(this.ios_options, function(opts){
17578 opts.el.dom.removeAttribute('selected');
17580 if(opts.data[this.valueField] != v){
17584 opts.el.dom.setAttribute('selected', true);
17590 * @cfg {Boolean} grow
17594 * @cfg {Number} growMin
17598 * @cfg {Number} growMax
17607 Roo.apply(Roo.bootstrap.ComboBox, {
17611 cls: 'modal-header',
17633 cls: 'list-group-item',
17637 cls: 'roo-combobox-list-group-item-value'
17641 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17655 listItemCheckbox : {
17657 cls: 'list-group-item',
17661 cls: 'roo-combobox-list-group-item-value'
17665 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17681 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17686 cls: 'modal-footer',
17694 cls: 'col-xs-6 text-left',
17697 cls: 'btn btn-danger roo-touch-view-cancel',
17703 cls: 'col-xs-6 text-right',
17706 cls: 'btn btn-success roo-touch-view-ok',
17717 Roo.apply(Roo.bootstrap.ComboBox, {
17719 touchViewTemplate : {
17721 cls: 'modal fade roo-combobox-touch-view',
17725 cls: 'modal-dialog',
17726 style : 'position:fixed', // we have to fix position....
17730 cls: 'modal-content',
17732 Roo.bootstrap.ComboBox.header,
17733 Roo.bootstrap.ComboBox.body,
17734 Roo.bootstrap.ComboBox.footer
17743 * Ext JS Library 1.1.1
17744 * Copyright(c) 2006-2007, Ext JS, LLC.
17746 * Originally Released Under LGPL - original licence link has changed is not relivant.
17749 * <script type="text/javascript">
17754 * @extends Roo.util.Observable
17755 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17756 * This class also supports single and multi selection modes. <br>
17757 * Create a data model bound view:
17759 var store = new Roo.data.Store(...);
17761 var view = new Roo.View({
17763 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17765 singleSelect: true,
17766 selectedClass: "ydataview-selected",
17770 // listen for node click?
17771 view.on("click", function(vw, index, node, e){
17772 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17776 dataModel.load("foobar.xml");
17778 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17780 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17781 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17783 * Note: old style constructor is still suported (container, template, config)
17786 * Create a new View
17787 * @param {Object} config The config object
17790 Roo.View = function(config, depreciated_tpl, depreciated_config){
17792 this.parent = false;
17794 if (typeof(depreciated_tpl) == 'undefined') {
17795 // new way.. - universal constructor.
17796 Roo.apply(this, config);
17797 this.el = Roo.get(this.el);
17800 this.el = Roo.get(config);
17801 this.tpl = depreciated_tpl;
17802 Roo.apply(this, depreciated_config);
17804 this.wrapEl = this.el.wrap().wrap();
17805 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17808 if(typeof(this.tpl) == "string"){
17809 this.tpl = new Roo.Template(this.tpl);
17811 // support xtype ctors..
17812 this.tpl = new Roo.factory(this.tpl, Roo);
17816 this.tpl.compile();
17821 * @event beforeclick
17822 * Fires before a click is processed. Returns false to cancel the default action.
17823 * @param {Roo.View} this
17824 * @param {Number} index The index of the target node
17825 * @param {HTMLElement} node The target node
17826 * @param {Roo.EventObject} e The raw event object
17828 "beforeclick" : true,
17831 * Fires when a template node is clicked.
17832 * @param {Roo.View} this
17833 * @param {Number} index The index of the target node
17834 * @param {HTMLElement} node The target node
17835 * @param {Roo.EventObject} e The raw event object
17840 * Fires when a template node is double clicked.
17841 * @param {Roo.View} this
17842 * @param {Number} index The index of the target node
17843 * @param {HTMLElement} node The target node
17844 * @param {Roo.EventObject} e The raw event object
17848 * @event contextmenu
17849 * Fires when a template node is right clicked.
17850 * @param {Roo.View} this
17851 * @param {Number} index The index of the target node
17852 * @param {HTMLElement} node The target node
17853 * @param {Roo.EventObject} e The raw event object
17855 "contextmenu" : true,
17857 * @event selectionchange
17858 * Fires when the selected nodes change.
17859 * @param {Roo.View} this
17860 * @param {Array} selections Array of the selected nodes
17862 "selectionchange" : true,
17865 * @event beforeselect
17866 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17867 * @param {Roo.View} this
17868 * @param {HTMLElement} node The node to be selected
17869 * @param {Array} selections Array of currently selected nodes
17871 "beforeselect" : true,
17873 * @event preparedata
17874 * Fires on every row to render, to allow you to change the data.
17875 * @param {Roo.View} this
17876 * @param {Object} data to be rendered (change this)
17878 "preparedata" : true
17886 "click": this.onClick,
17887 "dblclick": this.onDblClick,
17888 "contextmenu": this.onContextMenu,
17892 this.selections = [];
17894 this.cmp = new Roo.CompositeElementLite([]);
17896 this.store = Roo.factory(this.store, Roo.data);
17897 this.setStore(this.store, true);
17900 if ( this.footer && this.footer.xtype) {
17902 var fctr = this.wrapEl.appendChild(document.createElement("div"));
17904 this.footer.dataSource = this.store;
17905 this.footer.container = fctr;
17906 this.footer = Roo.factory(this.footer, Roo);
17907 fctr.insertFirst(this.el);
17909 // this is a bit insane - as the paging toolbar seems to detach the el..
17910 // dom.parentNode.parentNode.parentNode
17911 // they get detached?
17915 Roo.View.superclass.constructor.call(this);
17920 Roo.extend(Roo.View, Roo.util.Observable, {
17923 * @cfg {Roo.data.Store} store Data store to load data from.
17928 * @cfg {String|Roo.Element} el The container element.
17933 * @cfg {String|Roo.Template} tpl The template used by this View
17937 * @cfg {String} dataName the named area of the template to use as the data area
17938 * Works with domtemplates roo-name="name"
17942 * @cfg {String} selectedClass The css class to add to selected nodes
17944 selectedClass : "x-view-selected",
17946 * @cfg {String} emptyText The empty text to show when nothing is loaded.
17951 * @cfg {String} text to display on mask (default Loading)
17955 * @cfg {Boolean} multiSelect Allow multiple selection
17957 multiSelect : false,
17959 * @cfg {Boolean} singleSelect Allow single selection
17961 singleSelect: false,
17964 * @cfg {Boolean} toggleSelect - selecting
17966 toggleSelect : false,
17969 * @cfg {Boolean} tickable - selecting
17974 * Returns the element this view is bound to.
17975 * @return {Roo.Element}
17977 getEl : function(){
17978 return this.wrapEl;
17984 * Refreshes the view. - called by datachanged on the store. - do not call directly.
17986 refresh : function(){
17987 //Roo.log('refresh');
17990 // if we are using something like 'domtemplate', then
17991 // the what gets used is:
17992 // t.applySubtemplate(NAME, data, wrapping data..)
17993 // the outer template then get' applied with
17994 // the store 'extra data'
17995 // and the body get's added to the
17996 // roo-name="data" node?
17997 // <span class='roo-tpl-{name}'></span> ?????
18001 this.clearSelections();
18002 this.el.update("");
18004 var records = this.store.getRange();
18005 if(records.length < 1) {
18007 // is this valid?? = should it render a template??
18009 this.el.update(this.emptyText);
18013 if (this.dataName) {
18014 this.el.update(t.apply(this.store.meta)); //????
18015 el = this.el.child('.roo-tpl-' + this.dataName);
18018 for(var i = 0, len = records.length; i < len; i++){
18019 var data = this.prepareData(records[i].data, i, records[i]);
18020 this.fireEvent("preparedata", this, data, i, records[i]);
18022 var d = Roo.apply({}, data);
18025 Roo.apply(d, {'roo-id' : Roo.id()});
18029 Roo.each(this.parent.item, function(item){
18030 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18033 Roo.apply(d, {'roo-data-checked' : 'checked'});
18037 html[html.length] = Roo.util.Format.trim(
18039 t.applySubtemplate(this.dataName, d, this.store.meta) :
18046 el.update(html.join(""));
18047 this.nodes = el.dom.childNodes;
18048 this.updateIndexes(0);
18053 * Function to override to reformat the data that is sent to
18054 * the template for each node.
18055 * DEPRICATED - use the preparedata event handler.
18056 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18057 * a JSON object for an UpdateManager bound view).
18059 prepareData : function(data, index, record)
18061 this.fireEvent("preparedata", this, data, index, record);
18065 onUpdate : function(ds, record){
18066 // Roo.log('on update');
18067 this.clearSelections();
18068 var index = this.store.indexOf(record);
18069 var n = this.nodes[index];
18070 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18071 n.parentNode.removeChild(n);
18072 this.updateIndexes(index, index);
18078 onAdd : function(ds, records, index)
18080 //Roo.log(['on Add', ds, records, index] );
18081 this.clearSelections();
18082 if(this.nodes.length == 0){
18086 var n = this.nodes[index];
18087 for(var i = 0, len = records.length; i < len; i++){
18088 var d = this.prepareData(records[i].data, i, records[i]);
18090 this.tpl.insertBefore(n, d);
18093 this.tpl.append(this.el, d);
18096 this.updateIndexes(index);
18099 onRemove : function(ds, record, index){
18100 // Roo.log('onRemove');
18101 this.clearSelections();
18102 var el = this.dataName ?
18103 this.el.child('.roo-tpl-' + this.dataName) :
18106 el.dom.removeChild(this.nodes[index]);
18107 this.updateIndexes(index);
18111 * Refresh an individual node.
18112 * @param {Number} index
18114 refreshNode : function(index){
18115 this.onUpdate(this.store, this.store.getAt(index));
18118 updateIndexes : function(startIndex, endIndex){
18119 var ns = this.nodes;
18120 startIndex = startIndex || 0;
18121 endIndex = endIndex || ns.length - 1;
18122 for(var i = startIndex; i <= endIndex; i++){
18123 ns[i].nodeIndex = i;
18128 * Changes the data store this view uses and refresh the view.
18129 * @param {Store} store
18131 setStore : function(store, initial){
18132 if(!initial && this.store){
18133 this.store.un("datachanged", this.refresh);
18134 this.store.un("add", this.onAdd);
18135 this.store.un("remove", this.onRemove);
18136 this.store.un("update", this.onUpdate);
18137 this.store.un("clear", this.refresh);
18138 this.store.un("beforeload", this.onBeforeLoad);
18139 this.store.un("load", this.onLoad);
18140 this.store.un("loadexception", this.onLoad);
18144 store.on("datachanged", this.refresh, this);
18145 store.on("add", this.onAdd, this);
18146 store.on("remove", this.onRemove, this);
18147 store.on("update", this.onUpdate, this);
18148 store.on("clear", this.refresh, this);
18149 store.on("beforeload", this.onBeforeLoad, this);
18150 store.on("load", this.onLoad, this);
18151 store.on("loadexception", this.onLoad, this);
18159 * onbeforeLoad - masks the loading area.
18162 onBeforeLoad : function(store,opts)
18164 //Roo.log('onBeforeLoad');
18166 this.el.update("");
18168 this.el.mask(this.mask ? this.mask : "Loading" );
18170 onLoad : function ()
18177 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18178 * @param {HTMLElement} node
18179 * @return {HTMLElement} The template node
18181 findItemFromChild : function(node){
18182 var el = this.dataName ?
18183 this.el.child('.roo-tpl-' + this.dataName,true) :
18186 if(!node || node.parentNode == el){
18189 var p = node.parentNode;
18190 while(p && p != el){
18191 if(p.parentNode == el){
18200 onClick : function(e){
18201 var item = this.findItemFromChild(e.getTarget());
18203 var index = this.indexOf(item);
18204 if(this.onItemClick(item, index, e) !== false){
18205 this.fireEvent("click", this, index, item, e);
18208 this.clearSelections();
18213 onContextMenu : function(e){
18214 var item = this.findItemFromChild(e.getTarget());
18216 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18221 onDblClick : function(e){
18222 var item = this.findItemFromChild(e.getTarget());
18224 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18228 onItemClick : function(item, index, e)
18230 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18233 if (this.toggleSelect) {
18234 var m = this.isSelected(item) ? 'unselect' : 'select';
18237 _t[m](item, true, false);
18240 if(this.multiSelect || this.singleSelect){
18241 if(this.multiSelect && e.shiftKey && this.lastSelection){
18242 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18244 this.select(item, this.multiSelect && e.ctrlKey);
18245 this.lastSelection = item;
18248 if(!this.tickable){
18249 e.preventDefault();
18257 * Get the number of selected nodes.
18260 getSelectionCount : function(){
18261 return this.selections.length;
18265 * Get the currently selected nodes.
18266 * @return {Array} An array of HTMLElements
18268 getSelectedNodes : function(){
18269 return this.selections;
18273 * Get the indexes of the selected nodes.
18276 getSelectedIndexes : function(){
18277 var indexes = [], s = this.selections;
18278 for(var i = 0, len = s.length; i < len; i++){
18279 indexes.push(s[i].nodeIndex);
18285 * Clear all selections
18286 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18288 clearSelections : function(suppressEvent){
18289 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18290 this.cmp.elements = this.selections;
18291 this.cmp.removeClass(this.selectedClass);
18292 this.selections = [];
18293 if(!suppressEvent){
18294 this.fireEvent("selectionchange", this, this.selections);
18300 * Returns true if the passed node is selected
18301 * @param {HTMLElement/Number} node The node or node index
18302 * @return {Boolean}
18304 isSelected : function(node){
18305 var s = this.selections;
18309 node = this.getNode(node);
18310 return s.indexOf(node) !== -1;
18315 * @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
18316 * @param {Boolean} keepExisting (optional) true to keep existing selections
18317 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18319 select : function(nodeInfo, keepExisting, suppressEvent){
18320 if(nodeInfo instanceof Array){
18322 this.clearSelections(true);
18324 for(var i = 0, len = nodeInfo.length; i < len; i++){
18325 this.select(nodeInfo[i], true, true);
18329 var node = this.getNode(nodeInfo);
18330 if(!node || this.isSelected(node)){
18331 return; // already selected.
18334 this.clearSelections(true);
18337 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18338 Roo.fly(node).addClass(this.selectedClass);
18339 this.selections.push(node);
18340 if(!suppressEvent){
18341 this.fireEvent("selectionchange", this, this.selections);
18349 * @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
18350 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18351 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18353 unselect : function(nodeInfo, keepExisting, suppressEvent)
18355 if(nodeInfo instanceof Array){
18356 Roo.each(this.selections, function(s) {
18357 this.unselect(s, nodeInfo);
18361 var node = this.getNode(nodeInfo);
18362 if(!node || !this.isSelected(node)){
18363 //Roo.log("not selected");
18364 return; // not selected.
18368 Roo.each(this.selections, function(s) {
18370 Roo.fly(node).removeClass(this.selectedClass);
18377 this.selections= ns;
18378 this.fireEvent("selectionchange", this, this.selections);
18382 * Gets a template node.
18383 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18384 * @return {HTMLElement} The node or null if it wasn't found
18386 getNode : function(nodeInfo){
18387 if(typeof nodeInfo == "string"){
18388 return document.getElementById(nodeInfo);
18389 }else if(typeof nodeInfo == "number"){
18390 return this.nodes[nodeInfo];
18396 * Gets a range template nodes.
18397 * @param {Number} startIndex
18398 * @param {Number} endIndex
18399 * @return {Array} An array of nodes
18401 getNodes : function(start, end){
18402 var ns = this.nodes;
18403 start = start || 0;
18404 end = typeof end == "undefined" ? ns.length - 1 : end;
18407 for(var i = start; i <= end; i++){
18411 for(var i = start; i >= end; i--){
18419 * Finds the index of the passed node
18420 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18421 * @return {Number} The index of the node or -1
18423 indexOf : function(node){
18424 node = this.getNode(node);
18425 if(typeof node.nodeIndex == "number"){
18426 return node.nodeIndex;
18428 var ns = this.nodes;
18429 for(var i = 0, len = ns.length; i < len; i++){
18440 * based on jquery fullcalendar
18444 Roo.bootstrap = Roo.bootstrap || {};
18446 * @class Roo.bootstrap.Calendar
18447 * @extends Roo.bootstrap.Component
18448 * Bootstrap Calendar class
18449 * @cfg {Boolean} loadMask (true|false) default false
18450 * @cfg {Object} header generate the user specific header of the calendar, default false
18453 * Create a new Container
18454 * @param {Object} config The config object
18459 Roo.bootstrap.Calendar = function(config){
18460 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18464 * Fires when a date is selected
18465 * @param {DatePicker} this
18466 * @param {Date} date The selected date
18470 * @event monthchange
18471 * Fires when the displayed month changes
18472 * @param {DatePicker} this
18473 * @param {Date} date The selected month
18475 'monthchange': true,
18477 * @event evententer
18478 * Fires when mouse over an event
18479 * @param {Calendar} this
18480 * @param {event} Event
18482 'evententer': true,
18484 * @event eventleave
18485 * Fires when the mouse leaves an
18486 * @param {Calendar} this
18489 'eventleave': true,
18491 * @event eventclick
18492 * Fires when the mouse click an
18493 * @param {Calendar} this
18502 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18505 * @cfg {Number} startDay
18506 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18514 getAutoCreate : function(){
18517 var fc_button = function(name, corner, style, content ) {
18518 return Roo.apply({},{
18520 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18522 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18525 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18536 style : 'width:100%',
18543 cls : 'fc-header-left',
18545 fc_button('prev', 'left', 'arrow', '‹' ),
18546 fc_button('next', 'right', 'arrow', '›' ),
18547 { tag: 'span', cls: 'fc-header-space' },
18548 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18556 cls : 'fc-header-center',
18560 cls: 'fc-header-title',
18563 html : 'month / year'
18571 cls : 'fc-header-right',
18573 /* fc_button('month', 'left', '', 'month' ),
18574 fc_button('week', '', '', 'week' ),
18575 fc_button('day', 'right', '', 'day' )
18587 header = this.header;
18590 var cal_heads = function() {
18592 // fixme - handle this.
18594 for (var i =0; i < Date.dayNames.length; i++) {
18595 var d = Date.dayNames[i];
18598 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18599 html : d.substring(0,3)
18603 ret[0].cls += ' fc-first';
18604 ret[6].cls += ' fc-last';
18607 var cal_cell = function(n) {
18610 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18615 cls: 'fc-day-number',
18619 cls: 'fc-day-content',
18623 style: 'position: relative;' // height: 17px;
18635 var cal_rows = function() {
18638 for (var r = 0; r < 6; r++) {
18645 for (var i =0; i < Date.dayNames.length; i++) {
18646 var d = Date.dayNames[i];
18647 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18650 row.cn[0].cls+=' fc-first';
18651 row.cn[0].cn[0].style = 'min-height:90px';
18652 row.cn[6].cls+=' fc-last';
18656 ret[0].cls += ' fc-first';
18657 ret[4].cls += ' fc-prev-last';
18658 ret[5].cls += ' fc-last';
18665 cls: 'fc-border-separate',
18666 style : 'width:100%',
18674 cls : 'fc-first fc-last',
18692 cls : 'fc-content',
18693 style : "position: relative;",
18696 cls : 'fc-view fc-view-month fc-grid',
18697 style : 'position: relative',
18698 unselectable : 'on',
18701 cls : 'fc-event-container',
18702 style : 'position:absolute;z-index:8;top:0;left:0;'
18720 initEvents : function()
18723 throw "can not find store for calendar";
18729 style: "text-align:center",
18733 style: "background-color:white;width:50%;margin:250 auto",
18737 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18748 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18750 var size = this.el.select('.fc-content', true).first().getSize();
18751 this.maskEl.setSize(size.width, size.height);
18752 this.maskEl.enableDisplayMode("block");
18753 if(!this.loadMask){
18754 this.maskEl.hide();
18757 this.store = Roo.factory(this.store, Roo.data);
18758 this.store.on('load', this.onLoad, this);
18759 this.store.on('beforeload', this.onBeforeLoad, this);
18763 this.cells = this.el.select('.fc-day',true);
18764 //Roo.log(this.cells);
18765 this.textNodes = this.el.query('.fc-day-number');
18766 this.cells.addClassOnOver('fc-state-hover');
18768 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18769 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18770 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18771 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18773 this.on('monthchange', this.onMonthChange, this);
18775 this.update(new Date().clearTime());
18778 resize : function() {
18779 var sz = this.el.getSize();
18781 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18782 this.el.select('.fc-day-content div',true).setHeight(34);
18787 showPrevMonth : function(e){
18788 this.update(this.activeDate.add("mo", -1));
18790 showToday : function(e){
18791 this.update(new Date().clearTime());
18794 showNextMonth : function(e){
18795 this.update(this.activeDate.add("mo", 1));
18799 showPrevYear : function(){
18800 this.update(this.activeDate.add("y", -1));
18804 showNextYear : function(){
18805 this.update(this.activeDate.add("y", 1));
18810 update : function(date)
18812 var vd = this.activeDate;
18813 this.activeDate = date;
18814 // if(vd && this.el){
18815 // var t = date.getTime();
18816 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18817 // Roo.log('using add remove');
18819 // this.fireEvent('monthchange', this, date);
18821 // this.cells.removeClass("fc-state-highlight");
18822 // this.cells.each(function(c){
18823 // if(c.dateValue == t){
18824 // c.addClass("fc-state-highlight");
18825 // setTimeout(function(){
18826 // try{c.dom.firstChild.focus();}catch(e){}
18836 var days = date.getDaysInMonth();
18838 var firstOfMonth = date.getFirstDateOfMonth();
18839 var startingPos = firstOfMonth.getDay()-this.startDay;
18841 if(startingPos < this.startDay){
18845 var pm = date.add(Date.MONTH, -1);
18846 var prevStart = pm.getDaysInMonth()-startingPos;
18848 this.cells = this.el.select('.fc-day',true);
18849 this.textNodes = this.el.query('.fc-day-number');
18850 this.cells.addClassOnOver('fc-state-hover');
18852 var cells = this.cells.elements;
18853 var textEls = this.textNodes;
18855 Roo.each(cells, function(cell){
18856 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18859 days += startingPos;
18861 // convert everything to numbers so it's fast
18862 var day = 86400000;
18863 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18866 //Roo.log(prevStart);
18868 var today = new Date().clearTime().getTime();
18869 var sel = date.clearTime().getTime();
18870 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18871 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18872 var ddMatch = this.disabledDatesRE;
18873 var ddText = this.disabledDatesText;
18874 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18875 var ddaysText = this.disabledDaysText;
18876 var format = this.format;
18878 var setCellClass = function(cal, cell){
18882 //Roo.log('set Cell Class');
18884 var t = d.getTime();
18888 cell.dateValue = t;
18890 cell.className += " fc-today";
18891 cell.className += " fc-state-highlight";
18892 cell.title = cal.todayText;
18895 // disable highlight in other month..
18896 //cell.className += " fc-state-highlight";
18901 cell.className = " fc-state-disabled";
18902 cell.title = cal.minText;
18906 cell.className = " fc-state-disabled";
18907 cell.title = cal.maxText;
18911 if(ddays.indexOf(d.getDay()) != -1){
18912 cell.title = ddaysText;
18913 cell.className = " fc-state-disabled";
18916 if(ddMatch && format){
18917 var fvalue = d.dateFormat(format);
18918 if(ddMatch.test(fvalue)){
18919 cell.title = ddText.replace("%0", fvalue);
18920 cell.className = " fc-state-disabled";
18924 if (!cell.initialClassName) {
18925 cell.initialClassName = cell.dom.className;
18928 cell.dom.className = cell.initialClassName + ' ' + cell.className;
18933 for(; i < startingPos; i++) {
18934 textEls[i].innerHTML = (++prevStart);
18935 d.setDate(d.getDate()+1);
18937 cells[i].className = "fc-past fc-other-month";
18938 setCellClass(this, cells[i]);
18943 for(; i < days; i++){
18944 intDay = i - startingPos + 1;
18945 textEls[i].innerHTML = (intDay);
18946 d.setDate(d.getDate()+1);
18948 cells[i].className = ''; // "x-date-active";
18949 setCellClass(this, cells[i]);
18953 for(; i < 42; i++) {
18954 textEls[i].innerHTML = (++extraDays);
18955 d.setDate(d.getDate()+1);
18957 cells[i].className = "fc-future fc-other-month";
18958 setCellClass(this, cells[i]);
18961 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18963 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18965 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18966 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18968 if(totalRows != 6){
18969 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18970 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18973 this.fireEvent('monthchange', this, date);
18977 if(!this.internalRender){
18978 var main = this.el.dom.firstChild;
18979 var w = main.offsetWidth;
18980 this.el.setWidth(w + this.el.getBorderWidth("lr"));
18981 Roo.fly(main).setWidth(w);
18982 this.internalRender = true;
18983 // opera does not respect the auto grow header center column
18984 // then, after it gets a width opera refuses to recalculate
18985 // without a second pass
18986 if(Roo.isOpera && !this.secondPass){
18987 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18988 this.secondPass = true;
18989 this.update.defer(10, this, [date]);
18996 findCell : function(dt) {
18997 dt = dt.clearTime().getTime();
18999 this.cells.each(function(c){
19000 //Roo.log("check " +c.dateValue + '?=' + dt);
19001 if(c.dateValue == dt){
19011 findCells : function(ev) {
19012 var s = ev.start.clone().clearTime().getTime();
19014 var e= ev.end.clone().clearTime().getTime();
19017 this.cells.each(function(c){
19018 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19020 if(c.dateValue > e){
19023 if(c.dateValue < s){
19032 // findBestRow: function(cells)
19036 // for (var i =0 ; i < cells.length;i++) {
19037 // ret = Math.max(cells[i].rows || 0,ret);
19044 addItem : function(ev)
19046 // look for vertical location slot in
19047 var cells = this.findCells(ev);
19049 // ev.row = this.findBestRow(cells);
19051 // work out the location.
19055 for(var i =0; i < cells.length; i++) {
19057 cells[i].row = cells[0].row;
19060 cells[i].row = cells[i].row + 1;
19070 if (crow.start.getY() == cells[i].getY()) {
19072 crow.end = cells[i];
19089 cells[0].events.push(ev);
19091 this.calevents.push(ev);
19094 clearEvents: function() {
19096 if(!this.calevents){
19100 Roo.each(this.cells.elements, function(c){
19106 Roo.each(this.calevents, function(e) {
19107 Roo.each(e.els, function(el) {
19108 el.un('mouseenter' ,this.onEventEnter, this);
19109 el.un('mouseleave' ,this.onEventLeave, this);
19114 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19120 renderEvents: function()
19124 this.cells.each(function(c) {
19133 if(c.row != c.events.length){
19134 r = 4 - (4 - (c.row - c.events.length));
19137 c.events = ev.slice(0, r);
19138 c.more = ev.slice(r);
19140 if(c.more.length && c.more.length == 1){
19141 c.events.push(c.more.pop());
19144 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19148 this.cells.each(function(c) {
19150 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19153 for (var e = 0; e < c.events.length; e++){
19154 var ev = c.events[e];
19155 var rows = ev.rows;
19157 for(var i = 0; i < rows.length; i++) {
19159 // how many rows should it span..
19162 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19163 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19165 unselectable : "on",
19168 cls: 'fc-event-inner',
19172 // cls: 'fc-event-time',
19173 // html : cells.length > 1 ? '' : ev.time
19177 cls: 'fc-event-title',
19178 html : String.format('{0}', ev.title)
19185 cls: 'ui-resizable-handle ui-resizable-e',
19186 html : '  '
19193 cfg.cls += ' fc-event-start';
19195 if ((i+1) == rows.length) {
19196 cfg.cls += ' fc-event-end';
19199 var ctr = _this.el.select('.fc-event-container',true).first();
19200 var cg = ctr.createChild(cfg);
19202 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19203 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19205 var r = (c.more.length) ? 1 : 0;
19206 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19207 cg.setWidth(ebox.right - sbox.x -2);
19209 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19210 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19211 cg.on('click', _this.onEventClick, _this, ev);
19222 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19223 style : 'position: absolute',
19224 unselectable : "on",
19227 cls: 'fc-event-inner',
19231 cls: 'fc-event-title',
19239 cls: 'ui-resizable-handle ui-resizable-e',
19240 html : '  '
19246 var ctr = _this.el.select('.fc-event-container',true).first();
19247 var cg = ctr.createChild(cfg);
19249 var sbox = c.select('.fc-day-content',true).first().getBox();
19250 var ebox = c.select('.fc-day-content',true).first().getBox();
19252 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19253 cg.setWidth(ebox.right - sbox.x -2);
19255 cg.on('click', _this.onMoreEventClick, _this, c.more);
19265 onEventEnter: function (e, el,event,d) {
19266 this.fireEvent('evententer', this, el, event);
19269 onEventLeave: function (e, el,event,d) {
19270 this.fireEvent('eventleave', this, el, event);
19273 onEventClick: function (e, el,event,d) {
19274 this.fireEvent('eventclick', this, el, event);
19277 onMonthChange: function () {
19281 onMoreEventClick: function(e, el, more)
19285 this.calpopover.placement = 'right';
19286 this.calpopover.setTitle('More');
19288 this.calpopover.setContent('');
19290 var ctr = this.calpopover.el.select('.popover-content', true).first();
19292 Roo.each(more, function(m){
19294 cls : 'fc-event-hori fc-event-draggable',
19297 var cg = ctr.createChild(cfg);
19299 cg.on('click', _this.onEventClick, _this, m);
19302 this.calpopover.show(el);
19307 onLoad: function ()
19309 this.calevents = [];
19312 if(this.store.getCount() > 0){
19313 this.store.data.each(function(d){
19316 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19317 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19318 time : d.data.start_time,
19319 title : d.data.title,
19320 description : d.data.description,
19321 venue : d.data.venue
19326 this.renderEvents();
19328 if(this.calevents.length && this.loadMask){
19329 this.maskEl.hide();
19333 onBeforeLoad: function()
19335 this.clearEvents();
19337 this.maskEl.show();
19351 * @class Roo.bootstrap.Popover
19352 * @extends Roo.bootstrap.Component
19353 * Bootstrap Popover class
19354 * @cfg {String} html contents of the popover (or false to use children..)
19355 * @cfg {String} title of popover (or false to hide)
19356 * @cfg {String} placement how it is placed
19357 * @cfg {String} trigger click || hover (or false to trigger manually)
19358 * @cfg {String} over what (parent or false to trigger manually.)
19359 * @cfg {Number} delay - delay before showing
19362 * Create a new Popover
19363 * @param {Object} config The config object
19366 Roo.bootstrap.Popover = function(config){
19367 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19373 * After the popover show
19375 * @param {Roo.bootstrap.Popover} this
19380 * After the popover hide
19382 * @param {Roo.bootstrap.Popover} this
19388 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19390 title: 'Fill in a title',
19393 placement : 'right',
19394 trigger : 'hover', // hover
19400 can_build_overlaid : false,
19402 getChildContainer : function()
19404 return this.el.select('.popover-content',true).first();
19407 getAutoCreate : function(){
19410 cls : 'popover roo-dynamic',
19411 style: 'display:block',
19417 cls : 'popover-inner',
19421 cls: 'popover-title popover-header',
19425 cls : 'popover-content popover-body',
19436 setTitle: function(str)
19439 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19441 setContent: function(str)
19444 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19446 // as it get's added to the bottom of the page.
19447 onRender : function(ct, position)
19449 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19451 var cfg = Roo.apply({}, this.getAutoCreate());
19455 cfg.cls += ' ' + this.cls;
19458 cfg.style = this.style;
19460 //Roo.log("adding to ");
19461 this.el = Roo.get(document.body).createChild(cfg, position);
19462 // Roo.log(this.el);
19467 initEvents : function()
19469 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19470 this.el.enableDisplayMode('block');
19472 if (this.over === false) {
19475 if (this.triggers === false) {
19478 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19479 var triggers = this.trigger ? this.trigger.split(' ') : [];
19480 Roo.each(triggers, function(trigger) {
19482 if (trigger == 'click') {
19483 on_el.on('click', this.toggle, this);
19484 } else if (trigger != 'manual') {
19485 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19486 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19488 on_el.on(eventIn ,this.enter, this);
19489 on_el.on(eventOut, this.leave, this);
19500 toggle : function () {
19501 this.hoverState == 'in' ? this.leave() : this.enter();
19504 enter : function () {
19506 clearTimeout(this.timeout);
19508 this.hoverState = 'in';
19510 if (!this.delay || !this.delay.show) {
19515 this.timeout = setTimeout(function () {
19516 if (_t.hoverState == 'in') {
19519 }, this.delay.show)
19522 leave : function() {
19523 clearTimeout(this.timeout);
19525 this.hoverState = 'out';
19527 if (!this.delay || !this.delay.hide) {
19532 this.timeout = setTimeout(function () {
19533 if (_t.hoverState == 'out') {
19536 }, this.delay.hide)
19539 show : function (on_el)
19542 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19546 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19547 if (this.html !== false) {
19548 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19550 this.el.removeClass([
19551 'fade','top','bottom', 'left', 'right','in',
19552 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19554 if (!this.title.length) {
19555 this.el.select('.popover-title',true).hide();
19558 var placement = typeof this.placement == 'function' ?
19559 this.placement.call(this, this.el, on_el) :
19562 var autoToken = /\s?auto?\s?/i;
19563 var autoPlace = autoToken.test(placement);
19565 placement = placement.replace(autoToken, '') || 'top';
19569 //this.el.setXY([0,0]);
19571 this.el.dom.style.display='block';
19572 this.el.addClass(placement);
19574 //this.el.appendTo(on_el);
19576 var p = this.getPosition();
19577 var box = this.el.getBox();
19582 var align = Roo.bootstrap.Popover.alignment[placement];
19585 this.el.alignTo(on_el, align[0],align[1]);
19586 //var arrow = this.el.select('.arrow',true).first();
19587 //arrow.set(align[2],
19589 this.el.addClass('in');
19592 if (this.el.hasClass('fade')) {
19596 this.hoverState = 'in';
19598 this.fireEvent('show', this);
19603 this.el.setXY([0,0]);
19604 this.el.removeClass('in');
19606 this.hoverState = null;
19608 this.fireEvent('hide', this);
19613 Roo.bootstrap.Popover.alignment = {
19614 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19615 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19616 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19617 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19628 * @class Roo.bootstrap.Progress
19629 * @extends Roo.bootstrap.Component
19630 * Bootstrap Progress class
19631 * @cfg {Boolean} striped striped of the progress bar
19632 * @cfg {Boolean} active animated of the progress bar
19636 * Create a new Progress
19637 * @param {Object} config The config object
19640 Roo.bootstrap.Progress = function(config){
19641 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19644 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19649 getAutoCreate : function(){
19657 cfg.cls += ' progress-striped';
19661 cfg.cls += ' active';
19680 * @class Roo.bootstrap.ProgressBar
19681 * @extends Roo.bootstrap.Component
19682 * Bootstrap ProgressBar class
19683 * @cfg {Number} aria_valuenow aria-value now
19684 * @cfg {Number} aria_valuemin aria-value min
19685 * @cfg {Number} aria_valuemax aria-value max
19686 * @cfg {String} label label for the progress bar
19687 * @cfg {String} panel (success | info | warning | danger )
19688 * @cfg {String} role role of the progress bar
19689 * @cfg {String} sr_only text
19693 * Create a new ProgressBar
19694 * @param {Object} config The config object
19697 Roo.bootstrap.ProgressBar = function(config){
19698 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19701 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19705 aria_valuemax : 100,
19711 getAutoCreate : function()
19716 cls: 'progress-bar',
19717 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19729 cfg.role = this.role;
19732 if(this.aria_valuenow){
19733 cfg['aria-valuenow'] = this.aria_valuenow;
19736 if(this.aria_valuemin){
19737 cfg['aria-valuemin'] = this.aria_valuemin;
19740 if(this.aria_valuemax){
19741 cfg['aria-valuemax'] = this.aria_valuemax;
19744 if(this.label && !this.sr_only){
19745 cfg.html = this.label;
19749 cfg.cls += ' progress-bar-' + this.panel;
19755 update : function(aria_valuenow)
19757 this.aria_valuenow = aria_valuenow;
19759 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19774 * @class Roo.bootstrap.TabGroup
19775 * @extends Roo.bootstrap.Column
19776 * Bootstrap Column class
19777 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19778 * @cfg {Boolean} carousel true to make the group behave like a carousel
19779 * @cfg {Boolean} bullets show bullets for the panels
19780 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19781 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19782 * @cfg {Boolean} showarrow (true|false) show arrow default true
19785 * Create a new TabGroup
19786 * @param {Object} config The config object
19789 Roo.bootstrap.TabGroup = function(config){
19790 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19792 this.navId = Roo.id();
19795 Roo.bootstrap.TabGroup.register(this);
19799 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19802 transition : false,
19807 slideOnTouch : false,
19810 getAutoCreate : function()
19812 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19814 cfg.cls += ' tab-content';
19816 if (this.carousel) {
19817 cfg.cls += ' carousel slide';
19820 cls : 'carousel-inner',
19824 if(this.bullets && !Roo.isTouch){
19827 cls : 'carousel-bullets',
19831 if(this.bullets_cls){
19832 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19839 cfg.cn[0].cn.push(bullets);
19842 if(this.showarrow){
19843 cfg.cn[0].cn.push({
19845 class : 'carousel-arrow',
19849 class : 'carousel-prev',
19853 class : 'fa fa-chevron-left'
19859 class : 'carousel-next',
19863 class : 'fa fa-chevron-right'
19876 initEvents: function()
19878 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19879 // this.el.on("touchstart", this.onTouchStart, this);
19882 if(this.autoslide){
19885 this.slideFn = window.setInterval(function() {
19886 _this.showPanelNext();
19890 if(this.showarrow){
19891 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19892 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19898 // onTouchStart : function(e, el, o)
19900 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19904 // this.showPanelNext();
19908 getChildContainer : function()
19910 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19914 * register a Navigation item
19915 * @param {Roo.bootstrap.NavItem} the navitem to add
19917 register : function(item)
19919 this.tabs.push( item);
19920 item.navId = this.navId; // not really needed..
19925 getActivePanel : function()
19928 Roo.each(this.tabs, function(t) {
19938 getPanelByName : function(n)
19941 Roo.each(this.tabs, function(t) {
19942 if (t.tabId == n) {
19950 indexOfPanel : function(p)
19953 Roo.each(this.tabs, function(t,i) {
19954 if (t.tabId == p.tabId) {
19963 * show a specific panel
19964 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19965 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19967 showPanel : function (pan)
19969 if(this.transition || typeof(pan) == 'undefined'){
19970 Roo.log("waiting for the transitionend");
19974 if (typeof(pan) == 'number') {
19975 pan = this.tabs[pan];
19978 if (typeof(pan) == 'string') {
19979 pan = this.getPanelByName(pan);
19982 var cur = this.getActivePanel();
19985 Roo.log('pan or acitve pan is undefined');
19989 if (pan.tabId == this.getActivePanel().tabId) {
19993 if (false === cur.fireEvent('beforedeactivate')) {
19997 if(this.bullets > 0 && !Roo.isTouch){
19998 this.setActiveBullet(this.indexOfPanel(pan));
20001 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20003 //class="carousel-item carousel-item-next carousel-item-left"
20005 this.transition = true;
20006 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20007 var lr = dir == 'next' ? 'left' : 'right';
20008 pan.el.addClass(dir); // or prev
20009 pan.el.addClass('carousel-item-' + dir); // or prev
20010 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20011 cur.el.addClass(lr); // or right
20012 pan.el.addClass(lr);
20013 cur.el.addClass('carousel-item-' +lr); // or right
20014 pan.el.addClass('carousel-item-' +lr);
20018 cur.el.on('transitionend', function() {
20019 Roo.log("trans end?");
20021 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20022 pan.setActive(true);
20024 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20025 cur.setActive(false);
20027 _this.transition = false;
20029 }, this, { single: true } );
20034 cur.setActive(false);
20035 pan.setActive(true);
20040 showPanelNext : function()
20042 var i = this.indexOfPanel(this.getActivePanel());
20044 if (i >= this.tabs.length - 1 && !this.autoslide) {
20048 if (i >= this.tabs.length - 1 && this.autoslide) {
20052 this.showPanel(this.tabs[i+1]);
20055 showPanelPrev : function()
20057 var i = this.indexOfPanel(this.getActivePanel());
20059 if (i < 1 && !this.autoslide) {
20063 if (i < 1 && this.autoslide) {
20064 i = this.tabs.length;
20067 this.showPanel(this.tabs[i-1]);
20071 addBullet: function()
20073 if(!this.bullets || Roo.isTouch){
20076 var ctr = this.el.select('.carousel-bullets',true).first();
20077 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20078 var bullet = ctr.createChild({
20079 cls : 'bullet bullet-' + i
20080 },ctr.dom.lastChild);
20085 bullet.on('click', (function(e, el, o, ii, t){
20087 e.preventDefault();
20089 this.showPanel(ii);
20091 if(this.autoslide && this.slideFn){
20092 clearInterval(this.slideFn);
20093 this.slideFn = window.setInterval(function() {
20094 _this.showPanelNext();
20098 }).createDelegate(this, [i, bullet], true));
20103 setActiveBullet : function(i)
20109 Roo.each(this.el.select('.bullet', true).elements, function(el){
20110 el.removeClass('selected');
20113 var bullet = this.el.select('.bullet-' + i, true).first();
20119 bullet.addClass('selected');
20130 Roo.apply(Roo.bootstrap.TabGroup, {
20134 * register a Navigation Group
20135 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20137 register : function(navgrp)
20139 this.groups[navgrp.navId] = navgrp;
20143 * fetch a Navigation Group based on the navigation ID
20144 * if one does not exist , it will get created.
20145 * @param {string} the navgroup to add
20146 * @returns {Roo.bootstrap.NavGroup} the navgroup
20148 get: function(navId) {
20149 if (typeof(this.groups[navId]) == 'undefined') {
20150 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20152 return this.groups[navId] ;
20167 * @class Roo.bootstrap.TabPanel
20168 * @extends Roo.bootstrap.Component
20169 * Bootstrap TabPanel class
20170 * @cfg {Boolean} active panel active
20171 * @cfg {String} html panel content
20172 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20173 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20174 * @cfg {String} href click to link..
20178 * Create a new TabPanel
20179 * @param {Object} config The config object
20182 Roo.bootstrap.TabPanel = function(config){
20183 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20187 * Fires when the active status changes
20188 * @param {Roo.bootstrap.TabPanel} this
20189 * @param {Boolean} state the new state
20194 * @event beforedeactivate
20195 * Fires before a tab is de-activated - can be used to do validation on a form.
20196 * @param {Roo.bootstrap.TabPanel} this
20197 * @return {Boolean} false if there is an error
20200 'beforedeactivate': true
20203 this.tabId = this.tabId || Roo.id();
20207 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20215 getAutoCreate : function(){
20220 // item is needed for carousel - not sure if it has any effect otherwise
20221 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20222 html: this.html || ''
20226 cfg.cls += ' active';
20230 cfg.tabId = this.tabId;
20238 initEvents: function()
20240 var p = this.parent();
20242 this.navId = this.navId || p.navId;
20244 if (typeof(this.navId) != 'undefined') {
20245 // not really needed.. but just in case.. parent should be a NavGroup.
20246 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20250 var i = tg.tabs.length - 1;
20252 if(this.active && tg.bullets > 0 && i < tg.bullets){
20253 tg.setActiveBullet(i);
20257 this.el.on('click', this.onClick, this);
20260 this.el.on("touchstart", this.onTouchStart, this);
20261 this.el.on("touchmove", this.onTouchMove, this);
20262 this.el.on("touchend", this.onTouchEnd, this);
20267 onRender : function(ct, position)
20269 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20272 setActive : function(state)
20274 Roo.log("panel - set active " + this.tabId + "=" + state);
20276 this.active = state;
20278 this.el.removeClass('active');
20280 } else if (!this.el.hasClass('active')) {
20281 this.el.addClass('active');
20284 this.fireEvent('changed', this, state);
20287 onClick : function(e)
20289 e.preventDefault();
20291 if(!this.href.length){
20295 window.location.href = this.href;
20304 onTouchStart : function(e)
20306 this.swiping = false;
20308 this.startX = e.browserEvent.touches[0].clientX;
20309 this.startY = e.browserEvent.touches[0].clientY;
20312 onTouchMove : function(e)
20314 this.swiping = true;
20316 this.endX = e.browserEvent.touches[0].clientX;
20317 this.endY = e.browserEvent.touches[0].clientY;
20320 onTouchEnd : function(e)
20327 var tabGroup = this.parent();
20329 if(this.endX > this.startX){ // swiping right
20330 tabGroup.showPanelPrev();
20334 if(this.startX > this.endX){ // swiping left
20335 tabGroup.showPanelNext();
20354 * @class Roo.bootstrap.DateField
20355 * @extends Roo.bootstrap.Input
20356 * Bootstrap DateField class
20357 * @cfg {Number} weekStart default 0
20358 * @cfg {String} viewMode default empty, (months|years)
20359 * @cfg {String} minViewMode default empty, (months|years)
20360 * @cfg {Number} startDate default -Infinity
20361 * @cfg {Number} endDate default Infinity
20362 * @cfg {Boolean} todayHighlight default false
20363 * @cfg {Boolean} todayBtn default false
20364 * @cfg {Boolean} calendarWeeks default false
20365 * @cfg {Object} daysOfWeekDisabled default empty
20366 * @cfg {Boolean} singleMode default false (true | false)
20368 * @cfg {Boolean} keyboardNavigation default true
20369 * @cfg {String} language default en
20372 * Create a new DateField
20373 * @param {Object} config The config object
20376 Roo.bootstrap.DateField = function(config){
20377 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20381 * Fires when this field show.
20382 * @param {Roo.bootstrap.DateField} this
20383 * @param {Mixed} date The date value
20388 * Fires when this field hide.
20389 * @param {Roo.bootstrap.DateField} this
20390 * @param {Mixed} date The date value
20395 * Fires when select a date.
20396 * @param {Roo.bootstrap.DateField} this
20397 * @param {Mixed} date The date value
20401 * @event beforeselect
20402 * Fires when before select a date.
20403 * @param {Roo.bootstrap.DateField} this
20404 * @param {Mixed} date The date value
20406 beforeselect : true
20410 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20413 * @cfg {String} format
20414 * The default date format string which can be overriden for localization support. The format must be
20415 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20419 * @cfg {String} altFormats
20420 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20421 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20423 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20431 todayHighlight : false,
20437 keyboardNavigation: true,
20439 calendarWeeks: false,
20441 startDate: -Infinity,
20445 daysOfWeekDisabled: [],
20449 singleMode : false,
20451 UTCDate: function()
20453 return new Date(Date.UTC.apply(Date, arguments));
20456 UTCToday: function()
20458 var today = new Date();
20459 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20462 getDate: function() {
20463 var d = this.getUTCDate();
20464 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20467 getUTCDate: function() {
20471 setDate: function(d) {
20472 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20475 setUTCDate: function(d) {
20477 this.setValue(this.formatDate(this.date));
20480 onRender: function(ct, position)
20483 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20485 this.language = this.language || 'en';
20486 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20487 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20489 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20490 this.format = this.format || 'm/d/y';
20491 this.isInline = false;
20492 this.isInput = true;
20493 this.component = this.el.select('.add-on', true).first() || false;
20494 this.component = (this.component && this.component.length === 0) ? false : this.component;
20495 this.hasInput = this.component && this.inputEl().length;
20497 if (typeof(this.minViewMode === 'string')) {
20498 switch (this.minViewMode) {
20500 this.minViewMode = 1;
20503 this.minViewMode = 2;
20506 this.minViewMode = 0;
20511 if (typeof(this.viewMode === 'string')) {
20512 switch (this.viewMode) {
20525 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20527 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20529 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20531 this.picker().on('mousedown', this.onMousedown, this);
20532 this.picker().on('click', this.onClick, this);
20534 this.picker().addClass('datepicker-dropdown');
20536 this.startViewMode = this.viewMode;
20538 if(this.singleMode){
20539 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20540 v.setVisibilityMode(Roo.Element.DISPLAY);
20544 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20545 v.setStyle('width', '189px');
20549 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20550 if(!this.calendarWeeks){
20555 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20556 v.attr('colspan', function(i, val){
20557 return parseInt(val) + 1;
20562 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20564 this.setStartDate(this.startDate);
20565 this.setEndDate(this.endDate);
20567 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20574 if(this.isInline) {
20579 picker : function()
20581 return this.pickerEl;
20582 // return this.el.select('.datepicker', true).first();
20585 fillDow: function()
20587 var dowCnt = this.weekStart;
20596 if(this.calendarWeeks){
20604 while (dowCnt < this.weekStart + 7) {
20608 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20612 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20615 fillMonths: function()
20618 var months = this.picker().select('>.datepicker-months td', true).first();
20620 months.dom.innerHTML = '';
20626 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20629 months.createChild(month);
20636 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;
20638 if (this.date < this.startDate) {
20639 this.viewDate = new Date(this.startDate);
20640 } else if (this.date > this.endDate) {
20641 this.viewDate = new Date(this.endDate);
20643 this.viewDate = new Date(this.date);
20651 var d = new Date(this.viewDate),
20652 year = d.getUTCFullYear(),
20653 month = d.getUTCMonth(),
20654 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20655 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20656 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20657 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20658 currentDate = this.date && this.date.valueOf(),
20659 today = this.UTCToday();
20661 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20663 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20665 // this.picker.select('>tfoot th.today').
20666 // .text(dates[this.language].today)
20667 // .toggle(this.todayBtn !== false);
20669 this.updateNavArrows();
20672 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20674 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20676 prevMonth.setUTCDate(day);
20678 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20680 var nextMonth = new Date(prevMonth);
20682 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20684 nextMonth = nextMonth.valueOf();
20686 var fillMonths = false;
20688 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20690 while(prevMonth.valueOf() <= nextMonth) {
20693 if (prevMonth.getUTCDay() === this.weekStart) {
20695 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20703 if(this.calendarWeeks){
20704 // ISO 8601: First week contains first thursday.
20705 // ISO also states week starts on Monday, but we can be more abstract here.
20707 // Start of current week: based on weekstart/current date
20708 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20709 // Thursday of this week
20710 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20711 // First Thursday of year, year from thursday
20712 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20713 // Calendar week: ms between thursdays, div ms per day, div 7 days
20714 calWeek = (th - yth) / 864e5 / 7 + 1;
20716 fillMonths.cn.push({
20724 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20726 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20729 if (this.todayHighlight &&
20730 prevMonth.getUTCFullYear() == today.getFullYear() &&
20731 prevMonth.getUTCMonth() == today.getMonth() &&
20732 prevMonth.getUTCDate() == today.getDate()) {
20733 clsName += ' today';
20736 if (currentDate && prevMonth.valueOf() === currentDate) {
20737 clsName += ' active';
20740 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20741 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20742 clsName += ' disabled';
20745 fillMonths.cn.push({
20747 cls: 'day ' + clsName,
20748 html: prevMonth.getDate()
20751 prevMonth.setDate(prevMonth.getDate()+1);
20754 var currentYear = this.date && this.date.getUTCFullYear();
20755 var currentMonth = this.date && this.date.getUTCMonth();
20757 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20759 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20760 v.removeClass('active');
20762 if(currentYear === year && k === currentMonth){
20763 v.addClass('active');
20766 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20767 v.addClass('disabled');
20773 year = parseInt(year/10, 10) * 10;
20775 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20777 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20780 for (var i = -1; i < 11; i++) {
20781 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20783 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20791 showMode: function(dir)
20794 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20797 Roo.each(this.picker().select('>div',true).elements, function(v){
20798 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20801 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20806 if(this.isInline) {
20810 this.picker().removeClass(['bottom', 'top']);
20812 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20814 * place to the top of element!
20818 this.picker().addClass('top');
20819 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20824 this.picker().addClass('bottom');
20826 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20829 parseDate : function(value)
20831 if(!value || value instanceof Date){
20834 var v = Date.parseDate(value, this.format);
20835 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20836 v = Date.parseDate(value, 'Y-m-d');
20838 if(!v && this.altFormats){
20839 if(!this.altFormatsArray){
20840 this.altFormatsArray = this.altFormats.split("|");
20842 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20843 v = Date.parseDate(value, this.altFormatsArray[i]);
20849 formatDate : function(date, fmt)
20851 return (!date || !(date instanceof Date)) ?
20852 date : date.dateFormat(fmt || this.format);
20855 onFocus : function()
20857 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20861 onBlur : function()
20863 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20865 var d = this.inputEl().getValue();
20872 showPopup : function()
20874 this.picker().show();
20878 this.fireEvent('showpopup', this, this.date);
20881 hidePopup : function()
20883 if(this.isInline) {
20886 this.picker().hide();
20887 this.viewMode = this.startViewMode;
20890 this.fireEvent('hidepopup', this, this.date);
20894 onMousedown: function(e)
20896 e.stopPropagation();
20897 e.preventDefault();
20902 Roo.bootstrap.DateField.superclass.keyup.call(this);
20906 setValue: function(v)
20908 if(this.fireEvent('beforeselect', this, v) !== false){
20909 var d = new Date(this.parseDate(v) ).clearTime();
20911 if(isNaN(d.getTime())){
20912 this.date = this.viewDate = '';
20913 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20917 v = this.formatDate(d);
20919 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20921 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20925 this.fireEvent('select', this, this.date);
20929 getValue: function()
20931 return this.formatDate(this.date);
20934 fireKey: function(e)
20936 if (!this.picker().isVisible()){
20937 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20943 var dateChanged = false,
20945 newDate, newViewDate;
20950 e.preventDefault();
20954 if (!this.keyboardNavigation) {
20957 dir = e.keyCode == 37 ? -1 : 1;
20960 newDate = this.moveYear(this.date, dir);
20961 newViewDate = this.moveYear(this.viewDate, dir);
20962 } else if (e.shiftKey){
20963 newDate = this.moveMonth(this.date, dir);
20964 newViewDate = this.moveMonth(this.viewDate, dir);
20966 newDate = new Date(this.date);
20967 newDate.setUTCDate(this.date.getUTCDate() + dir);
20968 newViewDate = new Date(this.viewDate);
20969 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20971 if (this.dateWithinRange(newDate)){
20972 this.date = newDate;
20973 this.viewDate = newViewDate;
20974 this.setValue(this.formatDate(this.date));
20976 e.preventDefault();
20977 dateChanged = true;
20982 if (!this.keyboardNavigation) {
20985 dir = e.keyCode == 38 ? -1 : 1;
20987 newDate = this.moveYear(this.date, dir);
20988 newViewDate = this.moveYear(this.viewDate, dir);
20989 } else if (e.shiftKey){
20990 newDate = this.moveMonth(this.date, dir);
20991 newViewDate = this.moveMonth(this.viewDate, dir);
20993 newDate = new Date(this.date);
20994 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20995 newViewDate = new Date(this.viewDate);
20996 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20998 if (this.dateWithinRange(newDate)){
20999 this.date = newDate;
21000 this.viewDate = newViewDate;
21001 this.setValue(this.formatDate(this.date));
21003 e.preventDefault();
21004 dateChanged = true;
21008 this.setValue(this.formatDate(this.date));
21010 e.preventDefault();
21013 this.setValue(this.formatDate(this.date));
21027 onClick: function(e)
21029 e.stopPropagation();
21030 e.preventDefault();
21032 var target = e.getTarget();
21034 if(target.nodeName.toLowerCase() === 'i'){
21035 target = Roo.get(target).dom.parentNode;
21038 var nodeName = target.nodeName;
21039 var className = target.className;
21040 var html = target.innerHTML;
21041 //Roo.log(nodeName);
21043 switch(nodeName.toLowerCase()) {
21045 switch(className) {
21051 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21052 switch(this.viewMode){
21054 this.viewDate = this.moveMonth(this.viewDate, dir);
21058 this.viewDate = this.moveYear(this.viewDate, dir);
21064 var date = new Date();
21065 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21067 this.setValue(this.formatDate(this.date));
21074 if (className.indexOf('disabled') < 0) {
21075 this.viewDate.setUTCDate(1);
21076 if (className.indexOf('month') > -1) {
21077 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21079 var year = parseInt(html, 10) || 0;
21080 this.viewDate.setUTCFullYear(year);
21084 if(this.singleMode){
21085 this.setValue(this.formatDate(this.viewDate));
21096 //Roo.log(className);
21097 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21098 var day = parseInt(html, 10) || 1;
21099 var year = this.viewDate.getUTCFullYear(),
21100 month = this.viewDate.getUTCMonth();
21102 if (className.indexOf('old') > -1) {
21109 } else if (className.indexOf('new') > -1) {
21117 //Roo.log([year,month,day]);
21118 this.date = this.UTCDate(year, month, day,0,0,0,0);
21119 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21121 //Roo.log(this.formatDate(this.date));
21122 this.setValue(this.formatDate(this.date));
21129 setStartDate: function(startDate)
21131 this.startDate = startDate || -Infinity;
21132 if (this.startDate !== -Infinity) {
21133 this.startDate = this.parseDate(this.startDate);
21136 this.updateNavArrows();
21139 setEndDate: function(endDate)
21141 this.endDate = endDate || Infinity;
21142 if (this.endDate !== Infinity) {
21143 this.endDate = this.parseDate(this.endDate);
21146 this.updateNavArrows();
21149 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21151 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21152 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21153 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21155 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21156 return parseInt(d, 10);
21159 this.updateNavArrows();
21162 updateNavArrows: function()
21164 if(this.singleMode){
21168 var d = new Date(this.viewDate),
21169 year = d.getUTCFullYear(),
21170 month = d.getUTCMonth();
21172 Roo.each(this.picker().select('.prev', true).elements, function(v){
21174 switch (this.viewMode) {
21177 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21183 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21190 Roo.each(this.picker().select('.next', true).elements, function(v){
21192 switch (this.viewMode) {
21195 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21201 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21209 moveMonth: function(date, dir)
21214 var new_date = new Date(date.valueOf()),
21215 day = new_date.getUTCDate(),
21216 month = new_date.getUTCMonth(),
21217 mag = Math.abs(dir),
21219 dir = dir > 0 ? 1 : -1;
21222 // If going back one month, make sure month is not current month
21223 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21225 return new_date.getUTCMonth() == month;
21227 // If going forward one month, make sure month is as expected
21228 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21230 return new_date.getUTCMonth() != new_month;
21232 new_month = month + dir;
21233 new_date.setUTCMonth(new_month);
21234 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21235 if (new_month < 0 || new_month > 11) {
21236 new_month = (new_month + 12) % 12;
21239 // For magnitudes >1, move one month at a time...
21240 for (var i=0; i<mag; i++) {
21241 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21242 new_date = this.moveMonth(new_date, dir);
21244 // ...then reset the day, keeping it in the new month
21245 new_month = new_date.getUTCMonth();
21246 new_date.setUTCDate(day);
21248 return new_month != new_date.getUTCMonth();
21251 // Common date-resetting loop -- if date is beyond end of month, make it
21254 new_date.setUTCDate(--day);
21255 new_date.setUTCMonth(new_month);
21260 moveYear: function(date, dir)
21262 return this.moveMonth(date, dir*12);
21265 dateWithinRange: function(date)
21267 return date >= this.startDate && date <= this.endDate;
21273 this.picker().remove();
21276 validateValue : function(value)
21278 if(this.getVisibilityEl().hasClass('hidden')){
21282 if(value.length < 1) {
21283 if(this.allowBlank){
21289 if(value.length < this.minLength){
21292 if(value.length > this.maxLength){
21296 var vt = Roo.form.VTypes;
21297 if(!vt[this.vtype](value, this)){
21301 if(typeof this.validator == "function"){
21302 var msg = this.validator(value);
21308 if(this.regex && !this.regex.test(value)){
21312 if(typeof(this.parseDate(value)) == 'undefined'){
21316 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21320 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21330 this.date = this.viewDate = '';
21332 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21337 Roo.apply(Roo.bootstrap.DateField, {
21348 html: '<i class="fa fa-arrow-left"/>'
21358 html: '<i class="fa fa-arrow-right"/>'
21400 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21401 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21402 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21403 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21404 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21417 navFnc: 'FullYear',
21422 navFnc: 'FullYear',
21427 Roo.apply(Roo.bootstrap.DateField, {
21431 cls: 'datepicker dropdown-menu roo-dynamic',
21435 cls: 'datepicker-days',
21439 cls: 'table-condensed',
21441 Roo.bootstrap.DateField.head,
21445 Roo.bootstrap.DateField.footer
21452 cls: 'datepicker-months',
21456 cls: 'table-condensed',
21458 Roo.bootstrap.DateField.head,
21459 Roo.bootstrap.DateField.content,
21460 Roo.bootstrap.DateField.footer
21467 cls: 'datepicker-years',
21471 cls: 'table-condensed',
21473 Roo.bootstrap.DateField.head,
21474 Roo.bootstrap.DateField.content,
21475 Roo.bootstrap.DateField.footer
21494 * @class Roo.bootstrap.TimeField
21495 * @extends Roo.bootstrap.Input
21496 * Bootstrap DateField class
21500 * Create a new TimeField
21501 * @param {Object} config The config object
21504 Roo.bootstrap.TimeField = function(config){
21505 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21509 * Fires when this field show.
21510 * @param {Roo.bootstrap.DateField} thisthis
21511 * @param {Mixed} date The date value
21516 * Fires when this field hide.
21517 * @param {Roo.bootstrap.DateField} this
21518 * @param {Mixed} date The date value
21523 * Fires when select a date.
21524 * @param {Roo.bootstrap.DateField} this
21525 * @param {Mixed} date The date value
21531 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21534 * @cfg {String} format
21535 * The default time format string which can be overriden for localization support. The format must be
21536 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21540 onRender: function(ct, position)
21543 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21545 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21547 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21549 this.pop = this.picker().select('>.datepicker-time',true).first();
21550 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21552 this.picker().on('mousedown', this.onMousedown, this);
21553 this.picker().on('click', this.onClick, this);
21555 this.picker().addClass('datepicker-dropdown');
21560 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21561 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21562 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21563 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21564 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21565 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21569 fireKey: function(e){
21570 if (!this.picker().isVisible()){
21571 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21577 e.preventDefault();
21585 this.onTogglePeriod();
21588 this.onIncrementMinutes();
21591 this.onDecrementMinutes();
21600 onClick: function(e) {
21601 e.stopPropagation();
21602 e.preventDefault();
21605 picker : function()
21607 return this.el.select('.datepicker', true).first();
21610 fillTime: function()
21612 var time = this.pop.select('tbody', true).first();
21614 time.dom.innerHTML = '';
21629 cls: 'hours-up glyphicon glyphicon-chevron-up'
21649 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21670 cls: 'timepicker-hour',
21685 cls: 'timepicker-minute',
21700 cls: 'btn btn-primary period',
21722 cls: 'hours-down glyphicon glyphicon-chevron-down'
21742 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21760 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21767 var hours = this.time.getHours();
21768 var minutes = this.time.getMinutes();
21781 hours = hours - 12;
21785 hours = '0' + hours;
21789 minutes = '0' + minutes;
21792 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21793 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21794 this.pop.select('button', true).first().dom.innerHTML = period;
21800 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21802 var cls = ['bottom'];
21804 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21811 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21816 this.picker().addClass(cls.join('-'));
21820 Roo.each(cls, function(c){
21822 _this.picker().setTop(_this.inputEl().getHeight());
21826 _this.picker().setTop(0 - _this.picker().getHeight());
21831 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21835 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21842 onFocus : function()
21844 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21848 onBlur : function()
21850 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21856 this.picker().show();
21861 this.fireEvent('show', this, this.date);
21866 this.picker().hide();
21869 this.fireEvent('hide', this, this.date);
21872 setTime : function()
21875 this.setValue(this.time.format(this.format));
21877 this.fireEvent('select', this, this.date);
21882 onMousedown: function(e){
21883 e.stopPropagation();
21884 e.preventDefault();
21887 onIncrementHours: function()
21889 Roo.log('onIncrementHours');
21890 this.time = this.time.add(Date.HOUR, 1);
21895 onDecrementHours: function()
21897 Roo.log('onDecrementHours');
21898 this.time = this.time.add(Date.HOUR, -1);
21902 onIncrementMinutes: function()
21904 Roo.log('onIncrementMinutes');
21905 this.time = this.time.add(Date.MINUTE, 1);
21909 onDecrementMinutes: function()
21911 Roo.log('onDecrementMinutes');
21912 this.time = this.time.add(Date.MINUTE, -1);
21916 onTogglePeriod: function()
21918 Roo.log('onTogglePeriod');
21919 this.time = this.time.add(Date.HOUR, 12);
21926 Roo.apply(Roo.bootstrap.TimeField, {
21956 cls: 'btn btn-info ok',
21968 Roo.apply(Roo.bootstrap.TimeField, {
21972 cls: 'datepicker dropdown-menu',
21976 cls: 'datepicker-time',
21980 cls: 'table-condensed',
21982 Roo.bootstrap.TimeField.content,
21983 Roo.bootstrap.TimeField.footer
22002 * @class Roo.bootstrap.MonthField
22003 * @extends Roo.bootstrap.Input
22004 * Bootstrap MonthField class
22006 * @cfg {String} language default en
22009 * Create a new MonthField
22010 * @param {Object} config The config object
22013 Roo.bootstrap.MonthField = function(config){
22014 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22019 * Fires when this field show.
22020 * @param {Roo.bootstrap.MonthField} this
22021 * @param {Mixed} date The date value
22026 * Fires when this field hide.
22027 * @param {Roo.bootstrap.MonthField} this
22028 * @param {Mixed} date The date value
22033 * Fires when select a date.
22034 * @param {Roo.bootstrap.MonthField} this
22035 * @param {String} oldvalue The old value
22036 * @param {String} newvalue The new value
22042 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22044 onRender: function(ct, position)
22047 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22049 this.language = this.language || 'en';
22050 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22051 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22053 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22054 this.isInline = false;
22055 this.isInput = true;
22056 this.component = this.el.select('.add-on', true).first() || false;
22057 this.component = (this.component && this.component.length === 0) ? false : this.component;
22058 this.hasInput = this.component && this.inputEL().length;
22060 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22062 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22064 this.picker().on('mousedown', this.onMousedown, this);
22065 this.picker().on('click', this.onClick, this);
22067 this.picker().addClass('datepicker-dropdown');
22069 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22070 v.setStyle('width', '189px');
22077 if(this.isInline) {
22083 setValue: function(v, suppressEvent)
22085 var o = this.getValue();
22087 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22091 if(suppressEvent !== true){
22092 this.fireEvent('select', this, o, v);
22097 getValue: function()
22102 onClick: function(e)
22104 e.stopPropagation();
22105 e.preventDefault();
22107 var target = e.getTarget();
22109 if(target.nodeName.toLowerCase() === 'i'){
22110 target = Roo.get(target).dom.parentNode;
22113 var nodeName = target.nodeName;
22114 var className = target.className;
22115 var html = target.innerHTML;
22117 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22121 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22123 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22129 picker : function()
22131 return this.pickerEl;
22134 fillMonths: function()
22137 var months = this.picker().select('>.datepicker-months td', true).first();
22139 months.dom.innerHTML = '';
22145 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22148 months.createChild(month);
22157 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22158 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22161 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22162 e.removeClass('active');
22164 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22165 e.addClass('active');
22172 if(this.isInline) {
22176 this.picker().removeClass(['bottom', 'top']);
22178 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22180 * place to the top of element!
22184 this.picker().addClass('top');
22185 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22190 this.picker().addClass('bottom');
22192 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22195 onFocus : function()
22197 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22201 onBlur : function()
22203 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22205 var d = this.inputEl().getValue();
22214 this.picker().show();
22215 this.picker().select('>.datepicker-months', true).first().show();
22219 this.fireEvent('show', this, this.date);
22224 if(this.isInline) {
22227 this.picker().hide();
22228 this.fireEvent('hide', this, this.date);
22232 onMousedown: function(e)
22234 e.stopPropagation();
22235 e.preventDefault();
22240 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22244 fireKey: function(e)
22246 if (!this.picker().isVisible()){
22247 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22258 e.preventDefault();
22262 dir = e.keyCode == 37 ? -1 : 1;
22264 this.vIndex = this.vIndex + dir;
22266 if(this.vIndex < 0){
22270 if(this.vIndex > 11){
22274 if(isNaN(this.vIndex)){
22278 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22284 dir = e.keyCode == 38 ? -1 : 1;
22286 this.vIndex = this.vIndex + dir * 4;
22288 if(this.vIndex < 0){
22292 if(this.vIndex > 11){
22296 if(isNaN(this.vIndex)){
22300 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22305 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22306 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22310 e.preventDefault();
22313 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22314 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22330 this.picker().remove();
22335 Roo.apply(Roo.bootstrap.MonthField, {
22354 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22355 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22360 Roo.apply(Roo.bootstrap.MonthField, {
22364 cls: 'datepicker dropdown-menu roo-dynamic',
22368 cls: 'datepicker-months',
22372 cls: 'table-condensed',
22374 Roo.bootstrap.DateField.content
22394 * @class Roo.bootstrap.CheckBox
22395 * @extends Roo.bootstrap.Input
22396 * Bootstrap CheckBox class
22398 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22399 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22400 * @cfg {String} boxLabel The text that appears beside the checkbox
22401 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22402 * @cfg {Boolean} checked initnal the element
22403 * @cfg {Boolean} inline inline the element (default false)
22404 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22405 * @cfg {String} tooltip label tooltip
22408 * Create a new CheckBox
22409 * @param {Object} config The config object
22412 Roo.bootstrap.CheckBox = function(config){
22413 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22418 * Fires when the element is checked or unchecked.
22419 * @param {Roo.bootstrap.CheckBox} this This input
22420 * @param {Boolean} checked The new checked value
22425 * Fires when the element is click.
22426 * @param {Roo.bootstrap.CheckBox} this This input
22433 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22435 inputType: 'checkbox',
22444 // checkbox success does not make any sense really..
22449 getAutoCreate : function()
22451 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22457 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22460 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22466 type : this.inputType,
22467 value : this.inputValue,
22468 cls : 'roo-' + this.inputType, //'form-box',
22469 placeholder : this.placeholder || ''
22473 if(this.inputType != 'radio'){
22477 cls : 'roo-hidden-value',
22478 value : this.checked ? this.inputValue : this.valueOff
22483 if (this.weight) { // Validity check?
22484 cfg.cls += " " + this.inputType + "-" + this.weight;
22487 if (this.disabled) {
22488 input.disabled=true;
22492 input.checked = this.checked;
22497 input.name = this.name;
22499 if(this.inputType != 'radio'){
22500 hidden.name = this.name;
22501 input.name = '_hidden_' + this.name;
22506 input.cls += ' input-' + this.size;
22511 ['xs','sm','md','lg'].map(function(size){
22512 if (settings[size]) {
22513 cfg.cls += ' col-' + size + '-' + settings[size];
22517 var inputblock = input;
22519 if (this.before || this.after) {
22522 cls : 'input-group',
22527 inputblock.cn.push({
22529 cls : 'input-group-addon',
22534 inputblock.cn.push(input);
22536 if(this.inputType != 'radio'){
22537 inputblock.cn.push(hidden);
22541 inputblock.cn.push({
22543 cls : 'input-group-addon',
22549 var boxLabelCfg = false;
22555 //'for': id, // box label is handled by onclick - so no for...
22557 html: this.boxLabel
22560 boxLabelCfg.tooltip = this.tooltip;
22566 if (align ==='left' && this.fieldLabel.length) {
22567 // Roo.log("left and has label");
22572 cls : 'control-label',
22573 html : this.fieldLabel
22584 cfg.cn[1].cn.push(boxLabelCfg);
22587 if(this.labelWidth > 12){
22588 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22591 if(this.labelWidth < 13 && this.labelmd == 0){
22592 this.labelmd = this.labelWidth;
22595 if(this.labellg > 0){
22596 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22597 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22600 if(this.labelmd > 0){
22601 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22602 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22605 if(this.labelsm > 0){
22606 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22607 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22610 if(this.labelxs > 0){
22611 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22612 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22615 } else if ( this.fieldLabel.length) {
22616 // Roo.log(" label");
22620 tag: this.boxLabel ? 'span' : 'label',
22622 cls: 'control-label box-input-label',
22623 //cls : 'input-group-addon',
22624 html : this.fieldLabel
22631 cfg.cn.push(boxLabelCfg);
22636 // Roo.log(" no label && no align");
22637 cfg.cn = [ inputblock ] ;
22639 cfg.cn.push(boxLabelCfg);
22647 if(this.inputType != 'radio'){
22648 cfg.cn.push(hidden);
22656 * return the real input element.
22658 inputEl: function ()
22660 return this.el.select('input.roo-' + this.inputType,true).first();
22662 hiddenEl: function ()
22664 return this.el.select('input.roo-hidden-value',true).first();
22667 labelEl: function()
22669 return this.el.select('label.control-label',true).first();
22671 /* depricated... */
22675 return this.labelEl();
22678 boxLabelEl: function()
22680 return this.el.select('label.box-label',true).first();
22683 initEvents : function()
22685 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22687 this.inputEl().on('click', this.onClick, this);
22689 if (this.boxLabel) {
22690 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22693 this.startValue = this.getValue();
22696 Roo.bootstrap.CheckBox.register(this);
22700 onClick : function(e)
22702 if(this.fireEvent('click', this, e) !== false){
22703 this.setChecked(!this.checked);
22708 setChecked : function(state,suppressEvent)
22710 this.startValue = this.getValue();
22712 if(this.inputType == 'radio'){
22714 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22715 e.dom.checked = false;
22718 this.inputEl().dom.checked = true;
22720 this.inputEl().dom.value = this.inputValue;
22722 if(suppressEvent !== true){
22723 this.fireEvent('check', this, true);
22731 this.checked = state;
22733 this.inputEl().dom.checked = state;
22736 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22738 if(suppressEvent !== true){
22739 this.fireEvent('check', this, state);
22745 getValue : function()
22747 if(this.inputType == 'radio'){
22748 return this.getGroupValue();
22751 return this.hiddenEl().dom.value;
22755 getGroupValue : function()
22757 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22761 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22764 setValue : function(v,suppressEvent)
22766 if(this.inputType == 'radio'){
22767 this.setGroupValue(v, suppressEvent);
22771 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22776 setGroupValue : function(v, suppressEvent)
22778 this.startValue = this.getValue();
22780 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22781 e.dom.checked = false;
22783 if(e.dom.value == v){
22784 e.dom.checked = true;
22788 if(suppressEvent !== true){
22789 this.fireEvent('check', this, true);
22797 validate : function()
22799 if(this.getVisibilityEl().hasClass('hidden')){
22805 (this.inputType == 'radio' && this.validateRadio()) ||
22806 (this.inputType == 'checkbox' && this.validateCheckbox())
22812 this.markInvalid();
22816 validateRadio : function()
22818 if(this.getVisibilityEl().hasClass('hidden')){
22822 if(this.allowBlank){
22828 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22829 if(!e.dom.checked){
22841 validateCheckbox : function()
22844 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22845 //return (this.getValue() == this.inputValue) ? true : false;
22848 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22856 for(var i in group){
22857 if(group[i].el.isVisible(true)){
22865 for(var i in group){
22870 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22877 * Mark this field as valid
22879 markValid : function()
22883 this.fireEvent('valid', this);
22885 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22888 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22895 if(this.inputType == 'radio'){
22896 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22897 var fg = e.findParent('.form-group', false, true);
22898 if (Roo.bootstrap.version == 3) {
22899 fg.removeClass([_this.invalidClass, _this.validClass]);
22900 fg.addClass(_this.validClass);
22902 fg.removeClass(['is-valid', 'is-invalid']);
22903 fg.addClass('is-valid');
22911 var fg = this.el.findParent('.form-group', false, true);
22912 if (Roo.bootstrap.version == 3) {
22913 fg.removeClass([this.invalidClass, this.validClass]);
22914 fg.addClass(this.validClass);
22916 fg.removeClass(['is-valid', 'is-invalid']);
22917 fg.addClass('is-valid');
22922 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22928 for(var i in group){
22929 var fg = group[i].el.findParent('.form-group', false, true);
22930 if (Roo.bootstrap.version == 3) {
22931 fg.removeClass([this.invalidClass, this.validClass]);
22932 fg.addClass(this.validClass);
22934 fg.removeClass(['is-valid', 'is-invalid']);
22935 fg.addClass('is-valid');
22941 * Mark this field as invalid
22942 * @param {String} msg The validation message
22944 markInvalid : function(msg)
22946 if(this.allowBlank){
22952 this.fireEvent('invalid', this, msg);
22954 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22957 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22961 label.markInvalid();
22964 if(this.inputType == 'radio'){
22966 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22967 var fg = e.findParent('.form-group', false, true);
22968 if (Roo.bootstrap.version == 3) {
22969 fg.removeClass([_this.invalidClass, _this.validClass]);
22970 fg.addClass(_this.invalidClass);
22972 fg.removeClass(['is-invalid', 'is-valid']);
22973 fg.addClass('is-invalid');
22981 var fg = this.el.findParent('.form-group', false, true);
22982 if (Roo.bootstrap.version == 3) {
22983 fg.removeClass([_this.invalidClass, _this.validClass]);
22984 fg.addClass(_this.invalidClass);
22986 fg.removeClass(['is-invalid', 'is-valid']);
22987 fg.addClass('is-invalid');
22992 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22998 for(var i in group){
22999 var fg = group[i].el.findParent('.form-group', false, true);
23000 if (Roo.bootstrap.version == 3) {
23001 fg.removeClass([_this.invalidClass, _this.validClass]);
23002 fg.addClass(_this.invalidClass);
23004 fg.removeClass(['is-invalid', 'is-valid']);
23005 fg.addClass('is-invalid');
23011 clearInvalid : function()
23013 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23015 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23017 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23019 if (label && label.iconEl) {
23020 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23021 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23025 disable : function()
23027 if(this.inputType != 'radio'){
23028 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23035 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23036 _this.getActionEl().addClass(this.disabledClass);
23037 e.dom.disabled = true;
23041 this.disabled = true;
23042 this.fireEvent("disable", this);
23046 enable : function()
23048 if(this.inputType != 'radio'){
23049 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23056 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23057 _this.getActionEl().removeClass(this.disabledClass);
23058 e.dom.disabled = false;
23062 this.disabled = false;
23063 this.fireEvent("enable", this);
23067 setBoxLabel : function(v)
23072 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23078 Roo.apply(Roo.bootstrap.CheckBox, {
23083 * register a CheckBox Group
23084 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23086 register : function(checkbox)
23088 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23089 this.groups[checkbox.groupId] = {};
23092 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23096 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23100 * fetch a CheckBox Group based on the group ID
23101 * @param {string} the group ID
23102 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23104 get: function(groupId) {
23105 if (typeof(this.groups[groupId]) == 'undefined') {
23109 return this.groups[groupId] ;
23122 * @class Roo.bootstrap.Radio
23123 * @extends Roo.bootstrap.Component
23124 * Bootstrap Radio class
23125 * @cfg {String} boxLabel - the label associated
23126 * @cfg {String} value - the value of radio
23129 * Create a new Radio
23130 * @param {Object} config The config object
23132 Roo.bootstrap.Radio = function(config){
23133 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23137 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23143 getAutoCreate : function()
23147 cls : 'form-group radio',
23152 html : this.boxLabel
23160 initEvents : function()
23162 this.parent().register(this);
23164 this.el.on('click', this.onClick, this);
23168 onClick : function(e)
23170 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23171 this.setChecked(true);
23175 setChecked : function(state, suppressEvent)
23177 this.parent().setValue(this.value, suppressEvent);
23181 setBoxLabel : function(v)
23186 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23201 * @class Roo.bootstrap.SecurePass
23202 * @extends Roo.bootstrap.Input
23203 * Bootstrap SecurePass class
23207 * Create a new SecurePass
23208 * @param {Object} config The config object
23211 Roo.bootstrap.SecurePass = function (config) {
23212 // these go here, so the translation tool can replace them..
23214 PwdEmpty: "Please type a password, and then retype it to confirm.",
23215 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23216 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23217 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23218 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23219 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23220 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23221 TooWeak: "Your password is Too Weak."
23223 this.meterLabel = "Password strength:";
23224 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23225 this.meterClass = [
23226 "roo-password-meter-tooweak",
23227 "roo-password-meter-weak",
23228 "roo-password-meter-medium",
23229 "roo-password-meter-strong",
23230 "roo-password-meter-grey"
23235 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23238 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23240 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23242 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23243 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23244 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23245 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23246 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23247 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23248 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23258 * @cfg {String/Object} Label for the strength meter (defaults to
23259 * 'Password strength:')
23264 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23265 * ['Weak', 'Medium', 'Strong'])
23268 pwdStrengths: false,
23281 initEvents: function ()
23283 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23285 if (this.el.is('input[type=password]') && Roo.isSafari) {
23286 this.el.on('keydown', this.SafariOnKeyDown, this);
23289 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23292 onRender: function (ct, position)
23294 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23295 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23296 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23298 this.trigger.createChild({
23303 cls: 'roo-password-meter-grey col-xs-12',
23306 //width: this.meterWidth + 'px'
23310 cls: 'roo-password-meter-text'
23316 if (this.hideTrigger) {
23317 this.trigger.setDisplayed(false);
23319 this.setSize(this.width || '', this.height || '');
23322 onDestroy: function ()
23324 if (this.trigger) {
23325 this.trigger.removeAllListeners();
23326 this.trigger.remove();
23329 this.wrap.remove();
23331 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23334 checkStrength: function ()
23336 var pwd = this.inputEl().getValue();
23337 if (pwd == this._lastPwd) {
23342 if (this.ClientSideStrongPassword(pwd)) {
23344 } else if (this.ClientSideMediumPassword(pwd)) {
23346 } else if (this.ClientSideWeakPassword(pwd)) {
23352 Roo.log('strength1: ' + strength);
23354 //var pm = this.trigger.child('div/div/div').dom;
23355 var pm = this.trigger.child('div/div');
23356 pm.removeClass(this.meterClass);
23357 pm.addClass(this.meterClass[strength]);
23360 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23362 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23364 this._lastPwd = pwd;
23368 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23370 this._lastPwd = '';
23372 var pm = this.trigger.child('div/div');
23373 pm.removeClass(this.meterClass);
23374 pm.addClass('roo-password-meter-grey');
23377 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23380 this.inputEl().dom.type='password';
23383 validateValue: function (value)
23386 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23389 if (value.length == 0) {
23390 if (this.allowBlank) {
23391 this.clearInvalid();
23395 this.markInvalid(this.errors.PwdEmpty);
23396 this.errorMsg = this.errors.PwdEmpty;
23404 if ('[\x21-\x7e]*'.match(value)) {
23405 this.markInvalid(this.errors.PwdBadChar);
23406 this.errorMsg = this.errors.PwdBadChar;
23409 if (value.length < 6) {
23410 this.markInvalid(this.errors.PwdShort);
23411 this.errorMsg = this.errors.PwdShort;
23414 if (value.length > 16) {
23415 this.markInvalid(this.errors.PwdLong);
23416 this.errorMsg = this.errors.PwdLong;
23420 if (this.ClientSideStrongPassword(value)) {
23422 } else if (this.ClientSideMediumPassword(value)) {
23424 } else if (this.ClientSideWeakPassword(value)) {
23431 if (strength < 2) {
23432 //this.markInvalid(this.errors.TooWeak);
23433 this.errorMsg = this.errors.TooWeak;
23438 console.log('strength2: ' + strength);
23440 //var pm = this.trigger.child('div/div/div').dom;
23442 var pm = this.trigger.child('div/div');
23443 pm.removeClass(this.meterClass);
23444 pm.addClass(this.meterClass[strength]);
23446 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23448 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23450 this.errorMsg = '';
23454 CharacterSetChecks: function (type)
23457 this.fResult = false;
23460 isctype: function (character, type)
23463 case this.kCapitalLetter:
23464 if (character >= 'A' && character <= 'Z') {
23469 case this.kSmallLetter:
23470 if (character >= 'a' && character <= 'z') {
23476 if (character >= '0' && character <= '9') {
23481 case this.kPunctuation:
23482 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23493 IsLongEnough: function (pwd, size)
23495 return !(pwd == null || isNaN(size) || pwd.length < size);
23498 SpansEnoughCharacterSets: function (word, nb)
23500 if (!this.IsLongEnough(word, nb))
23505 var characterSetChecks = new Array(
23506 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23507 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23510 for (var index = 0; index < word.length; ++index) {
23511 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23512 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23513 characterSetChecks[nCharSet].fResult = true;
23520 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23521 if (characterSetChecks[nCharSet].fResult) {
23526 if (nCharSets < nb) {
23532 ClientSideStrongPassword: function (pwd)
23534 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23537 ClientSideMediumPassword: function (pwd)
23539 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23542 ClientSideWeakPassword: function (pwd)
23544 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23547 })//<script type="text/javascript">
23550 * Based Ext JS Library 1.1.1
23551 * Copyright(c) 2006-2007, Ext JS, LLC.
23557 * @class Roo.HtmlEditorCore
23558 * @extends Roo.Component
23559 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23561 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23564 Roo.HtmlEditorCore = function(config){
23567 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23572 * @event initialize
23573 * Fires when the editor is fully initialized (including the iframe)
23574 * @param {Roo.HtmlEditorCore} this
23579 * Fires when the editor is first receives the focus. Any insertion must wait
23580 * until after this event.
23581 * @param {Roo.HtmlEditorCore} this
23585 * @event beforesync
23586 * Fires before the textarea is updated with content from the editor iframe. Return false
23587 * to cancel the sync.
23588 * @param {Roo.HtmlEditorCore} this
23589 * @param {String} html
23593 * @event beforepush
23594 * Fires before the iframe editor is updated with content from the textarea. Return false
23595 * to cancel the push.
23596 * @param {Roo.HtmlEditorCore} this
23597 * @param {String} html
23602 * Fires when the textarea is updated with content from the editor iframe.
23603 * @param {Roo.HtmlEditorCore} this
23604 * @param {String} html
23609 * Fires when the iframe editor is updated with content from the textarea.
23610 * @param {Roo.HtmlEditorCore} this
23611 * @param {String} html
23616 * @event editorevent
23617 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23618 * @param {Roo.HtmlEditorCore} this
23624 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23626 // defaults : white / black...
23627 this.applyBlacklists();
23634 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23638 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23644 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23649 * @cfg {Number} height (in pixels)
23653 * @cfg {Number} width (in pixels)
23658 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23661 stylesheets: false,
23666 // private properties
23667 validationEvent : false,
23669 initialized : false,
23671 sourceEditMode : false,
23672 onFocus : Roo.emptyFn,
23674 hideMode:'offsets',
23678 // blacklist + whitelisted elements..
23685 * Protected method that will not generally be called directly. It
23686 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23687 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23689 getDocMarkup : function(){
23693 // inherit styels from page...??
23694 if (this.stylesheets === false) {
23696 Roo.get(document.head).select('style').each(function(node) {
23697 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23700 Roo.get(document.head).select('link').each(function(node) {
23701 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23704 } else if (!this.stylesheets.length) {
23706 st = '<style type="text/css">' +
23707 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23710 st = '<style type="text/css">' +
23715 st += '<style type="text/css">' +
23716 'IMG { cursor: pointer } ' +
23719 var cls = 'roo-htmleditor-body';
23721 if(this.bodyCls.length){
23722 cls += ' ' + this.bodyCls;
23725 return '<html><head>' + st +
23726 //<style type="text/css">' +
23727 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23729 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23733 onRender : function(ct, position)
23736 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23737 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23740 this.el.dom.style.border = '0 none';
23741 this.el.dom.setAttribute('tabIndex', -1);
23742 this.el.addClass('x-hidden hide');
23746 if(Roo.isIE){ // fix IE 1px bogus margin
23747 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23751 this.frameId = Roo.id();
23755 var iframe = this.owner.wrap.createChild({
23757 cls: 'form-control', // bootstrap..
23759 name: this.frameId,
23760 frameBorder : 'no',
23761 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23766 this.iframe = iframe.dom;
23768 this.assignDocWin();
23770 this.doc.designMode = 'on';
23773 this.doc.write(this.getDocMarkup());
23777 var task = { // must defer to wait for browser to be ready
23779 //console.log("run task?" + this.doc.readyState);
23780 this.assignDocWin();
23781 if(this.doc.body || this.doc.readyState == 'complete'){
23783 this.doc.designMode="on";
23787 Roo.TaskMgr.stop(task);
23788 this.initEditor.defer(10, this);
23795 Roo.TaskMgr.start(task);
23800 onResize : function(w, h)
23802 Roo.log('resize: ' +w + ',' + h );
23803 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23807 if(typeof w == 'number'){
23809 this.iframe.style.width = w + 'px';
23811 if(typeof h == 'number'){
23813 this.iframe.style.height = h + 'px';
23815 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23822 * Toggles the editor between standard and source edit mode.
23823 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23825 toggleSourceEdit : function(sourceEditMode){
23827 this.sourceEditMode = sourceEditMode === true;
23829 if(this.sourceEditMode){
23831 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23834 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23835 //this.iframe.className = '';
23838 //this.setSize(this.owner.wrap.getSize());
23839 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23846 * Protected method that will not generally be called directly. If you need/want
23847 * custom HTML cleanup, this is the method you should override.
23848 * @param {String} html The HTML to be cleaned
23849 * return {String} The cleaned HTML
23851 cleanHtml : function(html){
23852 html = String(html);
23853 if(html.length > 5){
23854 if(Roo.isSafari){ // strip safari nonsense
23855 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23858 if(html == ' '){
23865 * HTML Editor -> Textarea
23866 * Protected method that will not generally be called directly. Syncs the contents
23867 * of the editor iframe with the textarea.
23869 syncValue : function(){
23870 if(this.initialized){
23871 var bd = (this.doc.body || this.doc.documentElement);
23872 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23873 var html = bd.innerHTML;
23875 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23876 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23878 html = '<div style="'+m[0]+'">' + html + '</div>';
23881 html = this.cleanHtml(html);
23882 // fix up the special chars.. normaly like back quotes in word...
23883 // however we do not want to do this with chinese..
23884 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23886 var cc = match.charCodeAt();
23888 // Get the character value, handling surrogate pairs
23889 if (match.length == 2) {
23890 // It's a surrogate pair, calculate the Unicode code point
23891 var high = match.charCodeAt(0) - 0xD800;
23892 var low = match.charCodeAt(1) - 0xDC00;
23893 cc = (high * 0x400) + low + 0x10000;
23895 (cc >= 0x4E00 && cc < 0xA000 ) ||
23896 (cc >= 0x3400 && cc < 0x4E00 ) ||
23897 (cc >= 0xf900 && cc < 0xfb00 )
23902 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23903 return "&#" + cc + ";";
23910 if(this.owner.fireEvent('beforesync', this, html) !== false){
23911 this.el.dom.value = html;
23912 this.owner.fireEvent('sync', this, html);
23918 * Protected method that will not generally be called directly. Pushes the value of the textarea
23919 * into the iframe editor.
23921 pushValue : function(){
23922 if(this.initialized){
23923 var v = this.el.dom.value.trim();
23925 // if(v.length < 1){
23929 if(this.owner.fireEvent('beforepush', this, v) !== false){
23930 var d = (this.doc.body || this.doc.documentElement);
23932 this.cleanUpPaste();
23933 this.el.dom.value = d.innerHTML;
23934 this.owner.fireEvent('push', this, v);
23940 deferFocus : function(){
23941 this.focus.defer(10, this);
23945 focus : function(){
23946 if(this.win && !this.sourceEditMode){
23953 assignDocWin: function()
23955 var iframe = this.iframe;
23958 this.doc = iframe.contentWindow.document;
23959 this.win = iframe.contentWindow;
23961 // if (!Roo.get(this.frameId)) {
23964 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23965 // this.win = Roo.get(this.frameId).dom.contentWindow;
23967 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23971 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23972 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23977 initEditor : function(){
23978 //console.log("INIT EDITOR");
23979 this.assignDocWin();
23983 this.doc.designMode="on";
23985 this.doc.write(this.getDocMarkup());
23988 var dbody = (this.doc.body || this.doc.documentElement);
23989 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23990 // this copies styles from the containing element into thsi one..
23991 // not sure why we need all of this..
23992 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23994 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23995 //ss['background-attachment'] = 'fixed'; // w3c
23996 dbody.bgProperties = 'fixed'; // ie
23997 //Roo.DomHelper.applyStyles(dbody, ss);
23998 Roo.EventManager.on(this.doc, {
23999 //'mousedown': this.onEditorEvent,
24000 'mouseup': this.onEditorEvent,
24001 'dblclick': this.onEditorEvent,
24002 'click': this.onEditorEvent,
24003 'keyup': this.onEditorEvent,
24008 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24010 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24011 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24013 this.initialized = true;
24015 this.owner.fireEvent('initialize', this);
24020 onDestroy : function(){
24026 //for (var i =0; i < this.toolbars.length;i++) {
24027 // // fixme - ask toolbars for heights?
24028 // this.toolbars[i].onDestroy();
24031 //this.wrap.dom.innerHTML = '';
24032 //this.wrap.remove();
24037 onFirstFocus : function(){
24039 this.assignDocWin();
24042 this.activated = true;
24045 if(Roo.isGecko){ // prevent silly gecko errors
24047 var s = this.win.getSelection();
24048 if(!s.focusNode || s.focusNode.nodeType != 3){
24049 var r = s.getRangeAt(0);
24050 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24055 this.execCmd('useCSS', true);
24056 this.execCmd('styleWithCSS', false);
24059 this.owner.fireEvent('activate', this);
24063 adjustFont: function(btn){
24064 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24065 //if(Roo.isSafari){ // safari
24068 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24069 if(Roo.isSafari){ // safari
24070 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24071 v = (v < 10) ? 10 : v;
24072 v = (v > 48) ? 48 : v;
24073 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24078 v = Math.max(1, v+adjust);
24080 this.execCmd('FontSize', v );
24083 onEditorEvent : function(e)
24085 this.owner.fireEvent('editorevent', this, e);
24086 // this.updateToolbar();
24087 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24090 insertTag : function(tg)
24092 // could be a bit smarter... -> wrap the current selected tRoo..
24093 if (tg.toLowerCase() == 'span' ||
24094 tg.toLowerCase() == 'code' ||
24095 tg.toLowerCase() == 'sup' ||
24096 tg.toLowerCase() == 'sub'
24099 range = this.createRange(this.getSelection());
24100 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24101 wrappingNode.appendChild(range.extractContents());
24102 range.insertNode(wrappingNode);
24109 this.execCmd("formatblock", tg);
24113 insertText : function(txt)
24117 var range = this.createRange();
24118 range.deleteContents();
24119 //alert(Sender.getAttribute('label'));
24121 range.insertNode(this.doc.createTextNode(txt));
24127 * Executes a Midas editor command on the editor document and performs necessary focus and
24128 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24129 * @param {String} cmd The Midas command
24130 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24132 relayCmd : function(cmd, value){
24134 this.execCmd(cmd, value);
24135 this.owner.fireEvent('editorevent', this);
24136 //this.updateToolbar();
24137 this.owner.deferFocus();
24141 * Executes a Midas editor command directly on the editor document.
24142 * For visual commands, you should use {@link #relayCmd} instead.
24143 * <b>This should only be called after the editor is initialized.</b>
24144 * @param {String} cmd The Midas command
24145 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24147 execCmd : function(cmd, value){
24148 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24155 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24157 * @param {String} text | dom node..
24159 insertAtCursor : function(text)
24162 if(!this.activated){
24168 var r = this.doc.selection.createRange();
24179 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24183 // from jquery ui (MIT licenced)
24185 var win = this.win;
24187 if (win.getSelection && win.getSelection().getRangeAt) {
24188 range = win.getSelection().getRangeAt(0);
24189 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24190 range.insertNode(node);
24191 } else if (win.document.selection && win.document.selection.createRange) {
24192 // no firefox support
24193 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24194 win.document.selection.createRange().pasteHTML(txt);
24196 // no firefox support
24197 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24198 this.execCmd('InsertHTML', txt);
24207 mozKeyPress : function(e){
24209 var c = e.getCharCode(), cmd;
24212 c = String.fromCharCode(c).toLowerCase();
24226 this.cleanUpPaste.defer(100, this);
24234 e.preventDefault();
24242 fixKeys : function(){ // load time branching for fastest keydown performance
24244 return function(e){
24245 var k = e.getKey(), r;
24248 r = this.doc.selection.createRange();
24251 r.pasteHTML('    ');
24258 r = this.doc.selection.createRange();
24260 var target = r.parentElement();
24261 if(!target || target.tagName.toLowerCase() != 'li'){
24263 r.pasteHTML('<br />');
24269 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24270 this.cleanUpPaste.defer(100, this);
24276 }else if(Roo.isOpera){
24277 return function(e){
24278 var k = e.getKey();
24282 this.execCmd('InsertHTML','    ');
24285 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24286 this.cleanUpPaste.defer(100, this);
24291 }else if(Roo.isSafari){
24292 return function(e){
24293 var k = e.getKey();
24297 this.execCmd('InsertText','\t');
24301 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24302 this.cleanUpPaste.defer(100, this);
24310 getAllAncestors: function()
24312 var p = this.getSelectedNode();
24315 a.push(p); // push blank onto stack..
24316 p = this.getParentElement();
24320 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24324 a.push(this.doc.body);
24328 lastSelNode : false,
24331 getSelection : function()
24333 this.assignDocWin();
24334 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24337 getSelectedNode: function()
24339 // this may only work on Gecko!!!
24341 // should we cache this!!!!
24346 var range = this.createRange(this.getSelection()).cloneRange();
24349 var parent = range.parentElement();
24351 var testRange = range.duplicate();
24352 testRange.moveToElementText(parent);
24353 if (testRange.inRange(range)) {
24356 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24359 parent = parent.parentElement;
24364 // is ancestor a text element.
24365 var ac = range.commonAncestorContainer;
24366 if (ac.nodeType == 3) {
24367 ac = ac.parentNode;
24370 var ar = ac.childNodes;
24373 var other_nodes = [];
24374 var has_other_nodes = false;
24375 for (var i=0;i<ar.length;i++) {
24376 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24379 // fullly contained node.
24381 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24386 // probably selected..
24387 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24388 other_nodes.push(ar[i]);
24392 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24397 has_other_nodes = true;
24399 if (!nodes.length && other_nodes.length) {
24400 nodes= other_nodes;
24402 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24408 createRange: function(sel)
24410 // this has strange effects when using with
24411 // top toolbar - not sure if it's a great idea.
24412 //this.editor.contentWindow.focus();
24413 if (typeof sel != "undefined") {
24415 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24417 return this.doc.createRange();
24420 return this.doc.createRange();
24423 getParentElement: function()
24426 this.assignDocWin();
24427 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24429 var range = this.createRange(sel);
24432 var p = range.commonAncestorContainer;
24433 while (p.nodeType == 3) { // text node
24444 * Range intersection.. the hard stuff...
24448 * [ -- selected range --- ]
24452 * if end is before start or hits it. fail.
24453 * if start is after end or hits it fail.
24455 * if either hits (but other is outside. - then it's not
24461 // @see http://www.thismuchiknow.co.uk/?p=64.
24462 rangeIntersectsNode : function(range, node)
24464 var nodeRange = node.ownerDocument.createRange();
24466 nodeRange.selectNode(node);
24468 nodeRange.selectNodeContents(node);
24471 var rangeStartRange = range.cloneRange();
24472 rangeStartRange.collapse(true);
24474 var rangeEndRange = range.cloneRange();
24475 rangeEndRange.collapse(false);
24477 var nodeStartRange = nodeRange.cloneRange();
24478 nodeStartRange.collapse(true);
24480 var nodeEndRange = nodeRange.cloneRange();
24481 nodeEndRange.collapse(false);
24483 return rangeStartRange.compareBoundaryPoints(
24484 Range.START_TO_START, nodeEndRange) == -1 &&
24485 rangeEndRange.compareBoundaryPoints(
24486 Range.START_TO_START, nodeStartRange) == 1;
24490 rangeCompareNode : function(range, node)
24492 var nodeRange = node.ownerDocument.createRange();
24494 nodeRange.selectNode(node);
24496 nodeRange.selectNodeContents(node);
24500 range.collapse(true);
24502 nodeRange.collapse(true);
24504 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24505 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24507 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24509 var nodeIsBefore = ss == 1;
24510 var nodeIsAfter = ee == -1;
24512 if (nodeIsBefore && nodeIsAfter) {
24515 if (!nodeIsBefore && nodeIsAfter) {
24516 return 1; //right trailed.
24519 if (nodeIsBefore && !nodeIsAfter) {
24520 return 2; // left trailed.
24526 // private? - in a new class?
24527 cleanUpPaste : function()
24529 // cleans up the whole document..
24530 Roo.log('cleanuppaste');
24532 this.cleanUpChildren(this.doc.body);
24533 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24534 if (clean != this.doc.body.innerHTML) {
24535 this.doc.body.innerHTML = clean;
24540 cleanWordChars : function(input) {// change the chars to hex code
24541 var he = Roo.HtmlEditorCore;
24543 var output = input;
24544 Roo.each(he.swapCodes, function(sw) {
24545 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24547 output = output.replace(swapper, sw[1]);
24554 cleanUpChildren : function (n)
24556 if (!n.childNodes.length) {
24559 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24560 this.cleanUpChild(n.childNodes[i]);
24567 cleanUpChild : function (node)
24570 //console.log(node);
24571 if (node.nodeName == "#text") {
24572 // clean up silly Windows -- stuff?
24575 if (node.nodeName == "#comment") {
24576 node.parentNode.removeChild(node);
24577 // clean up silly Windows -- stuff?
24580 var lcname = node.tagName.toLowerCase();
24581 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24582 // whitelist of tags..
24584 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24586 node.parentNode.removeChild(node);
24591 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24593 // spans with no attributes - just remove them..
24594 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24595 remove_keep_children = true;
24598 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24599 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24601 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24602 // remove_keep_children = true;
24605 if (remove_keep_children) {
24606 this.cleanUpChildren(node);
24607 // inserts everything just before this node...
24608 while (node.childNodes.length) {
24609 var cn = node.childNodes[0];
24610 node.removeChild(cn);
24611 node.parentNode.insertBefore(cn, node);
24613 node.parentNode.removeChild(node);
24617 if (!node.attributes || !node.attributes.length) {
24622 this.cleanUpChildren(node);
24626 function cleanAttr(n,v)
24629 if (v.match(/^\./) || v.match(/^\//)) {
24632 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24635 if (v.match(/^#/)) {
24638 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24639 node.removeAttribute(n);
24643 var cwhite = this.cwhite;
24644 var cblack = this.cblack;
24646 function cleanStyle(n,v)
24648 if (v.match(/expression/)) { //XSS?? should we even bother..
24649 node.removeAttribute(n);
24653 var parts = v.split(/;/);
24656 Roo.each(parts, function(p) {
24657 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24661 var l = p.split(':').shift().replace(/\s+/g,'');
24662 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24664 if ( cwhite.length && cblack.indexOf(l) > -1) {
24665 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24666 //node.removeAttribute(n);
24670 // only allow 'c whitelisted system attributes'
24671 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24672 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24673 //node.removeAttribute(n);
24683 if (clean.length) {
24684 node.setAttribute(n, clean.join(';'));
24686 node.removeAttribute(n);
24692 for (var i = node.attributes.length-1; i > -1 ; i--) {
24693 var a = node.attributes[i];
24696 if (a.name.toLowerCase().substr(0,2)=='on') {
24697 node.removeAttribute(a.name);
24700 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24701 node.removeAttribute(a.name);
24704 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24705 cleanAttr(a.name,a.value); // fixme..
24708 if (a.name == 'style') {
24709 cleanStyle(a.name,a.value);
24712 /// clean up MS crap..
24713 // tecnically this should be a list of valid class'es..
24716 if (a.name == 'class') {
24717 if (a.value.match(/^Mso/)) {
24718 node.removeAttribute('class');
24721 if (a.value.match(/^body$/)) {
24722 node.removeAttribute('class');
24733 this.cleanUpChildren(node);
24739 * Clean up MS wordisms...
24741 cleanWord : function(node)
24744 this.cleanWord(this.doc.body);
24749 node.nodeName == 'SPAN' &&
24750 !node.hasAttributes() &&
24751 node.childNodes.length == 1 &&
24752 node.firstChild.nodeName == "#text"
24754 var textNode = node.firstChild;
24755 node.removeChild(textNode);
24756 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24757 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24759 node.parentNode.insertBefore(textNode, node);
24760 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24761 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24763 node.parentNode.removeChild(node);
24766 if (node.nodeName == "#text") {
24767 // clean up silly Windows -- stuff?
24770 if (node.nodeName == "#comment") {
24771 node.parentNode.removeChild(node);
24772 // clean up silly Windows -- stuff?
24776 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24777 node.parentNode.removeChild(node);
24780 //Roo.log(node.tagName);
24781 // remove - but keep children..
24782 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24783 //Roo.log('-- removed');
24784 while (node.childNodes.length) {
24785 var cn = node.childNodes[0];
24786 node.removeChild(cn);
24787 node.parentNode.insertBefore(cn, node);
24788 // move node to parent - and clean it..
24789 this.cleanWord(cn);
24791 node.parentNode.removeChild(node);
24792 /// no need to iterate chidlren = it's got none..
24793 //this.iterateChildren(node, this.cleanWord);
24797 if (node.className.length) {
24799 var cn = node.className.split(/\W+/);
24801 Roo.each(cn, function(cls) {
24802 if (cls.match(/Mso[a-zA-Z]+/)) {
24807 node.className = cna.length ? cna.join(' ') : '';
24809 node.removeAttribute("class");
24813 if (node.hasAttribute("lang")) {
24814 node.removeAttribute("lang");
24817 if (node.hasAttribute("style")) {
24819 var styles = node.getAttribute("style").split(";");
24821 Roo.each(styles, function(s) {
24822 if (!s.match(/:/)) {
24825 var kv = s.split(":");
24826 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24829 // what ever is left... we allow.
24832 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24833 if (!nstyle.length) {
24834 node.removeAttribute('style');
24837 this.iterateChildren(node, this.cleanWord);
24843 * iterateChildren of a Node, calling fn each time, using this as the scole..
24844 * @param {DomNode} node node to iterate children of.
24845 * @param {Function} fn method of this class to call on each item.
24847 iterateChildren : function(node, fn)
24849 if (!node.childNodes.length) {
24852 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24853 fn.call(this, node.childNodes[i])
24859 * cleanTableWidths.
24861 * Quite often pasting from word etc.. results in tables with column and widths.
24862 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24865 cleanTableWidths : function(node)
24870 this.cleanTableWidths(this.doc.body);
24875 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24878 Roo.log(node.tagName);
24879 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24880 this.iterateChildren(node, this.cleanTableWidths);
24883 if (node.hasAttribute('width')) {
24884 node.removeAttribute('width');
24888 if (node.hasAttribute("style")) {
24891 var styles = node.getAttribute("style").split(";");
24893 Roo.each(styles, function(s) {
24894 if (!s.match(/:/)) {
24897 var kv = s.split(":");
24898 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24901 // what ever is left... we allow.
24904 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24905 if (!nstyle.length) {
24906 node.removeAttribute('style');
24910 this.iterateChildren(node, this.cleanTableWidths);
24918 domToHTML : function(currentElement, depth, nopadtext) {
24920 depth = depth || 0;
24921 nopadtext = nopadtext || false;
24923 if (!currentElement) {
24924 return this.domToHTML(this.doc.body);
24927 //Roo.log(currentElement);
24929 var allText = false;
24930 var nodeName = currentElement.nodeName;
24931 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24933 if (nodeName == '#text') {
24935 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24940 if (nodeName != 'BODY') {
24943 // Prints the node tagName, such as <A>, <IMG>, etc
24946 for(i = 0; i < currentElement.attributes.length;i++) {
24948 var aname = currentElement.attributes.item(i).name;
24949 if (!currentElement.attributes.item(i).value.length) {
24952 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24955 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24964 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24967 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24972 // Traverse the tree
24974 var currentElementChild = currentElement.childNodes.item(i);
24975 var allText = true;
24976 var innerHTML = '';
24978 while (currentElementChild) {
24979 // Formatting code (indent the tree so it looks nice on the screen)
24980 var nopad = nopadtext;
24981 if (lastnode == 'SPAN') {
24985 if (currentElementChild.nodeName == '#text') {
24986 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24987 toadd = nopadtext ? toadd : toadd.trim();
24988 if (!nopad && toadd.length > 80) {
24989 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
24991 innerHTML += toadd;
24994 currentElementChild = currentElement.childNodes.item(i);
25000 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25002 // Recursively traverse the tree structure of the child node
25003 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25004 lastnode = currentElementChild.nodeName;
25006 currentElementChild=currentElement.childNodes.item(i);
25012 // The remaining code is mostly for formatting the tree
25013 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25018 ret+= "</"+tagName+">";
25024 applyBlacklists : function()
25026 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25027 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25031 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25032 if (b.indexOf(tag) > -1) {
25035 this.white.push(tag);
25039 Roo.each(w, function(tag) {
25040 if (b.indexOf(tag) > -1) {
25043 if (this.white.indexOf(tag) > -1) {
25046 this.white.push(tag);
25051 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25052 if (w.indexOf(tag) > -1) {
25055 this.black.push(tag);
25059 Roo.each(b, function(tag) {
25060 if (w.indexOf(tag) > -1) {
25063 if (this.black.indexOf(tag) > -1) {
25066 this.black.push(tag);
25071 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25072 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25076 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25077 if (b.indexOf(tag) > -1) {
25080 this.cwhite.push(tag);
25084 Roo.each(w, function(tag) {
25085 if (b.indexOf(tag) > -1) {
25088 if (this.cwhite.indexOf(tag) > -1) {
25091 this.cwhite.push(tag);
25096 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25097 if (w.indexOf(tag) > -1) {
25100 this.cblack.push(tag);
25104 Roo.each(b, function(tag) {
25105 if (w.indexOf(tag) > -1) {
25108 if (this.cblack.indexOf(tag) > -1) {
25111 this.cblack.push(tag);
25116 setStylesheets : function(stylesheets)
25118 if(typeof(stylesheets) == 'string'){
25119 Roo.get(this.iframe.contentDocument.head).createChild({
25121 rel : 'stylesheet',
25130 Roo.each(stylesheets, function(s) {
25135 Roo.get(_this.iframe.contentDocument.head).createChild({
25137 rel : 'stylesheet',
25146 removeStylesheets : function()
25150 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25155 setStyle : function(style)
25157 Roo.get(this.iframe.contentDocument.head).createChild({
25166 // hide stuff that is not compatible
25180 * @event specialkey
25184 * @cfg {String} fieldClass @hide
25187 * @cfg {String} focusClass @hide
25190 * @cfg {String} autoCreate @hide
25193 * @cfg {String} inputType @hide
25196 * @cfg {String} invalidClass @hide
25199 * @cfg {String} invalidText @hide
25202 * @cfg {String} msgFx @hide
25205 * @cfg {String} validateOnBlur @hide
25209 Roo.HtmlEditorCore.white = [
25210 'area', 'br', 'img', 'input', 'hr', 'wbr',
25212 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25213 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25214 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25215 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25216 'table', 'ul', 'xmp',
25218 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25221 'dir', 'menu', 'ol', 'ul', 'dl',
25227 Roo.HtmlEditorCore.black = [
25228 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25230 'base', 'basefont', 'bgsound', 'blink', 'body',
25231 'frame', 'frameset', 'head', 'html', 'ilayer',
25232 'iframe', 'layer', 'link', 'meta', 'object',
25233 'script', 'style' ,'title', 'xml' // clean later..
25235 Roo.HtmlEditorCore.clean = [
25236 'script', 'style', 'title', 'xml'
25238 Roo.HtmlEditorCore.remove = [
25243 Roo.HtmlEditorCore.ablack = [
25247 Roo.HtmlEditorCore.aclean = [
25248 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25252 Roo.HtmlEditorCore.pwhite= [
25253 'http', 'https', 'mailto'
25256 // white listed style attributes.
25257 Roo.HtmlEditorCore.cwhite= [
25258 // 'text-align', /// default is to allow most things..
25264 // black listed style attributes.
25265 Roo.HtmlEditorCore.cblack= [
25266 // 'font-size' -- this can be set by the project
25270 Roo.HtmlEditorCore.swapCodes =[
25289 * @class Roo.bootstrap.HtmlEditor
25290 * @extends Roo.bootstrap.TextArea
25291 * Bootstrap HtmlEditor class
25294 * Create a new HtmlEditor
25295 * @param {Object} config The config object
25298 Roo.bootstrap.HtmlEditor = function(config){
25299 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25300 if (!this.toolbars) {
25301 this.toolbars = [];
25304 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25307 * @event initialize
25308 * Fires when the editor is fully initialized (including the iframe)
25309 * @param {HtmlEditor} this
25314 * Fires when the editor is first receives the focus. Any insertion must wait
25315 * until after this event.
25316 * @param {HtmlEditor} this
25320 * @event beforesync
25321 * Fires before the textarea is updated with content from the editor iframe. Return false
25322 * to cancel the sync.
25323 * @param {HtmlEditor} this
25324 * @param {String} html
25328 * @event beforepush
25329 * Fires before the iframe editor is updated with content from the textarea. Return false
25330 * to cancel the push.
25331 * @param {HtmlEditor} this
25332 * @param {String} html
25337 * Fires when the textarea is updated with content from the editor iframe.
25338 * @param {HtmlEditor} this
25339 * @param {String} html
25344 * Fires when the iframe editor is updated with content from the textarea.
25345 * @param {HtmlEditor} this
25346 * @param {String} html
25350 * @event editmodechange
25351 * Fires when the editor switches edit modes
25352 * @param {HtmlEditor} this
25353 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25355 editmodechange: true,
25357 * @event editorevent
25358 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25359 * @param {HtmlEditor} this
25363 * @event firstfocus
25364 * Fires when on first focus - needed by toolbars..
25365 * @param {HtmlEditor} this
25370 * Auto save the htmlEditor value as a file into Events
25371 * @param {HtmlEditor} this
25375 * @event savedpreview
25376 * preview the saved version of htmlEditor
25377 * @param {HtmlEditor} this
25384 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25388 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25393 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25398 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25403 * @cfg {Number} height (in pixels)
25407 * @cfg {Number} width (in pixels)
25412 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25415 stylesheets: false,
25420 // private properties
25421 validationEvent : false,
25423 initialized : false,
25426 onFocus : Roo.emptyFn,
25428 hideMode:'offsets',
25430 tbContainer : false,
25434 toolbarContainer :function() {
25435 return this.wrap.select('.x-html-editor-tb',true).first();
25439 * Protected method that will not generally be called directly. It
25440 * is called when the editor creates its toolbar. Override this method if you need to
25441 * add custom toolbar buttons.
25442 * @param {HtmlEditor} editor
25444 createToolbar : function(){
25445 Roo.log('renewing');
25446 Roo.log("create toolbars");
25448 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25449 this.toolbars[0].render(this.toolbarContainer());
25453 // if (!editor.toolbars || !editor.toolbars.length) {
25454 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25457 // for (var i =0 ; i < editor.toolbars.length;i++) {
25458 // editor.toolbars[i] = Roo.factory(
25459 // typeof(editor.toolbars[i]) == 'string' ?
25460 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25461 // Roo.bootstrap.HtmlEditor);
25462 // editor.toolbars[i].init(editor);
25468 onRender : function(ct, position)
25470 // Roo.log("Call onRender: " + this.xtype);
25472 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25474 this.wrap = this.inputEl().wrap({
25475 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25478 this.editorcore.onRender(ct, position);
25480 if (this.resizable) {
25481 this.resizeEl = new Roo.Resizable(this.wrap, {
25485 minHeight : this.height,
25486 height: this.height,
25487 handles : this.resizable,
25490 resize : function(r, w, h) {
25491 _t.onResize(w,h); // -something
25497 this.createToolbar(this);
25500 if(!this.width && this.resizable){
25501 this.setSize(this.wrap.getSize());
25503 if (this.resizeEl) {
25504 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25505 // should trigger onReize..
25511 onResize : function(w, h)
25513 Roo.log('resize: ' +w + ',' + h );
25514 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25518 if(this.inputEl() ){
25519 if(typeof w == 'number'){
25520 var aw = w - this.wrap.getFrameWidth('lr');
25521 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25524 if(typeof h == 'number'){
25525 var tbh = -11; // fixme it needs to tool bar size!
25526 for (var i =0; i < this.toolbars.length;i++) {
25527 // fixme - ask toolbars for heights?
25528 tbh += this.toolbars[i].el.getHeight();
25529 //if (this.toolbars[i].footer) {
25530 // tbh += this.toolbars[i].footer.el.getHeight();
25538 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25539 ah -= 5; // knock a few pixes off for look..
25540 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25544 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25545 this.editorcore.onResize(ew,eh);
25550 * Toggles the editor between standard and source edit mode.
25551 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25553 toggleSourceEdit : function(sourceEditMode)
25555 this.editorcore.toggleSourceEdit(sourceEditMode);
25557 if(this.editorcore.sourceEditMode){
25558 Roo.log('editor - showing textarea');
25561 // Roo.log(this.syncValue());
25563 this.inputEl().removeClass(['hide', 'x-hidden']);
25564 this.inputEl().dom.removeAttribute('tabIndex');
25565 this.inputEl().focus();
25567 Roo.log('editor - hiding textarea');
25569 // Roo.log(this.pushValue());
25572 this.inputEl().addClass(['hide', 'x-hidden']);
25573 this.inputEl().dom.setAttribute('tabIndex', -1);
25574 //this.deferFocus();
25577 if(this.resizable){
25578 this.setSize(this.wrap.getSize());
25581 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25584 // private (for BoxComponent)
25585 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25587 // private (for BoxComponent)
25588 getResizeEl : function(){
25592 // private (for BoxComponent)
25593 getPositionEl : function(){
25598 initEvents : function(){
25599 this.originalValue = this.getValue();
25603 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25606 // markInvalid : Roo.emptyFn,
25608 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25611 // clearInvalid : Roo.emptyFn,
25613 setValue : function(v){
25614 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25615 this.editorcore.pushValue();
25620 deferFocus : function(){
25621 this.focus.defer(10, this);
25625 focus : function(){
25626 this.editorcore.focus();
25632 onDestroy : function(){
25638 for (var i =0; i < this.toolbars.length;i++) {
25639 // fixme - ask toolbars for heights?
25640 this.toolbars[i].onDestroy();
25643 this.wrap.dom.innerHTML = '';
25644 this.wrap.remove();
25649 onFirstFocus : function(){
25650 //Roo.log("onFirstFocus");
25651 this.editorcore.onFirstFocus();
25652 for (var i =0; i < this.toolbars.length;i++) {
25653 this.toolbars[i].onFirstFocus();
25659 syncValue : function()
25661 this.editorcore.syncValue();
25664 pushValue : function()
25666 this.editorcore.pushValue();
25670 // hide stuff that is not compatible
25684 * @event specialkey
25688 * @cfg {String} fieldClass @hide
25691 * @cfg {String} focusClass @hide
25694 * @cfg {String} autoCreate @hide
25697 * @cfg {String} inputType @hide
25701 * @cfg {String} invalidText @hide
25704 * @cfg {String} msgFx @hide
25707 * @cfg {String} validateOnBlur @hide
25716 Roo.namespace('Roo.bootstrap.htmleditor');
25718 * @class Roo.bootstrap.HtmlEditorToolbar1
25724 new Roo.bootstrap.HtmlEditor({
25727 new Roo.bootstrap.HtmlEditorToolbar1({
25728 disable : { fonts: 1 , format: 1, ..., ... , ...],
25734 * @cfg {Object} disable List of elements to disable..
25735 * @cfg {Array} btns List of additional buttons.
25739 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25742 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25745 Roo.apply(this, config);
25747 // default disabled, based on 'good practice'..
25748 this.disable = this.disable || {};
25749 Roo.applyIf(this.disable, {
25752 specialElements : true
25754 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25756 this.editor = config.editor;
25757 this.editorcore = config.editor.editorcore;
25759 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25761 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25762 // dont call parent... till later.
25764 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25769 editorcore : false,
25774 "h1","h2","h3","h4","h5","h6",
25776 "abbr", "acronym", "address", "cite", "samp", "var",
25780 onRender : function(ct, position)
25782 // Roo.log("Call onRender: " + this.xtype);
25784 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25786 this.el.dom.style.marginBottom = '0';
25788 var editorcore = this.editorcore;
25789 var editor= this.editor;
25792 var btn = function(id,cmd , toggle, handler, html){
25794 var event = toggle ? 'toggle' : 'click';
25799 xns: Roo.bootstrap,
25803 enableToggle:toggle !== false,
25805 pressed : toggle ? false : null,
25808 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25809 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25815 // var cb_box = function...
25820 xns: Roo.bootstrap,
25825 xns: Roo.bootstrap,
25829 Roo.each(this.formats, function(f) {
25830 style.menu.items.push({
25832 xns: Roo.bootstrap,
25833 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25838 editorcore.insertTag(this.tagname);
25845 children.push(style);
25847 btn('bold',false,true);
25848 btn('italic',false,true);
25849 btn('align-left', 'justifyleft',true);
25850 btn('align-center', 'justifycenter',true);
25851 btn('align-right' , 'justifyright',true);
25852 btn('link', false, false, function(btn) {
25853 //Roo.log("create link?");
25854 var url = prompt(this.createLinkText, this.defaultLinkValue);
25855 if(url && url != 'http:/'+'/'){
25856 this.editorcore.relayCmd('createlink', url);
25859 btn('list','insertunorderedlist',true);
25860 btn('pencil', false,true, function(btn){
25862 this.toggleSourceEdit(btn.pressed);
25865 if (this.editor.btns.length > 0) {
25866 for (var i = 0; i<this.editor.btns.length; i++) {
25867 children.push(this.editor.btns[i]);
25875 xns: Roo.bootstrap,
25880 xns: Roo.bootstrap,
25885 cog.menu.items.push({
25887 xns: Roo.bootstrap,
25888 html : Clean styles,
25893 editorcore.insertTag(this.tagname);
25902 this.xtype = 'NavSimplebar';
25904 for(var i=0;i< children.length;i++) {
25906 this.buttons.add(this.addxtypeChild(children[i]));
25910 editor.on('editorevent', this.updateToolbar, this);
25912 onBtnClick : function(id)
25914 this.editorcore.relayCmd(id);
25915 this.editorcore.focus();
25919 * Protected method that will not generally be called directly. It triggers
25920 * a toolbar update by reading the markup state of the current selection in the editor.
25922 updateToolbar: function(){
25924 if(!this.editorcore.activated){
25925 this.editor.onFirstFocus(); // is this neeed?
25929 var btns = this.buttons;
25930 var doc = this.editorcore.doc;
25931 btns.get('bold').setActive(doc.queryCommandState('bold'));
25932 btns.get('italic').setActive(doc.queryCommandState('italic'));
25933 //btns.get('underline').setActive(doc.queryCommandState('underline'));
25935 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25936 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25937 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25939 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25940 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25943 var ans = this.editorcore.getAllAncestors();
25944 if (this.formatCombo) {
25947 var store = this.formatCombo.store;
25948 this.formatCombo.setValue("");
25949 for (var i =0; i < ans.length;i++) {
25950 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25952 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25960 // hides menus... - so this cant be on a menu...
25961 Roo.bootstrap.MenuMgr.hideAll();
25963 Roo.bootstrap.MenuMgr.hideAll();
25964 //this.editorsyncValue();
25966 onFirstFocus: function() {
25967 this.buttons.each(function(item){
25971 toggleSourceEdit : function(sourceEditMode){
25974 if(sourceEditMode){
25975 Roo.log("disabling buttons");
25976 this.buttons.each( function(item){
25977 if(item.cmd != 'pencil'){
25983 Roo.log("enabling buttons");
25984 if(this.editorcore.initialized){
25985 this.buttons.each( function(item){
25991 Roo.log("calling toggole on editor");
25992 // tell the editor that it's been pressed..
25993 this.editor.toggleSourceEdit(sourceEditMode);
26003 * @class Roo.bootstrap.Table.AbstractSelectionModel
26004 * @extends Roo.util.Observable
26005 * Abstract base class for grid SelectionModels. It provides the interface that should be
26006 * implemented by descendant classes. This class should not be directly instantiated.
26009 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26010 this.locked = false;
26011 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26015 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26016 /** @ignore Called by the grid automatically. Do not call directly. */
26017 init : function(grid){
26023 * Locks the selections.
26026 this.locked = true;
26030 * Unlocks the selections.
26032 unlock : function(){
26033 this.locked = false;
26037 * Returns true if the selections are locked.
26038 * @return {Boolean}
26040 isLocked : function(){
26041 return this.locked;
26045 initEvents : function ()
26051 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26052 * @class Roo.bootstrap.Table.RowSelectionModel
26053 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26054 * It supports multiple selections and keyboard selection/navigation.
26056 * @param {Object} config
26059 Roo.bootstrap.Table.RowSelectionModel = function(config){
26060 Roo.apply(this, config);
26061 this.selections = new Roo.util.MixedCollection(false, function(o){
26066 this.lastActive = false;
26070 * @event selectionchange
26071 * Fires when the selection changes
26072 * @param {SelectionModel} this
26074 "selectionchange" : true,
26076 * @event afterselectionchange
26077 * Fires after the selection changes (eg. by key press or clicking)
26078 * @param {SelectionModel} this
26080 "afterselectionchange" : true,
26082 * @event beforerowselect
26083 * Fires when a row is selected being selected, return false to cancel.
26084 * @param {SelectionModel} this
26085 * @param {Number} rowIndex The selected index
26086 * @param {Boolean} keepExisting False if other selections will be cleared
26088 "beforerowselect" : true,
26091 * Fires when a row is selected.
26092 * @param {SelectionModel} this
26093 * @param {Number} rowIndex The selected index
26094 * @param {Roo.data.Record} r The record
26096 "rowselect" : true,
26098 * @event rowdeselect
26099 * Fires when a row is deselected.
26100 * @param {SelectionModel} this
26101 * @param {Number} rowIndex The selected index
26103 "rowdeselect" : true
26105 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26106 this.locked = false;
26109 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26111 * @cfg {Boolean} singleSelect
26112 * True to allow selection of only one row at a time (defaults to false)
26114 singleSelect : false,
26117 initEvents : function()
26120 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26121 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26122 //}else{ // allow click to work like normal
26123 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26125 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26126 this.grid.on("rowclick", this.handleMouseDown, this);
26128 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26129 "up" : function(e){
26131 this.selectPrevious(e.shiftKey);
26132 }else if(this.last !== false && this.lastActive !== false){
26133 var last = this.last;
26134 this.selectRange(this.last, this.lastActive-1);
26135 this.grid.getView().focusRow(this.lastActive);
26136 if(last !== false){
26140 this.selectFirstRow();
26142 this.fireEvent("afterselectionchange", this);
26144 "down" : function(e){
26146 this.selectNext(e.shiftKey);
26147 }else if(this.last !== false && this.lastActive !== false){
26148 var last = this.last;
26149 this.selectRange(this.last, this.lastActive+1);
26150 this.grid.getView().focusRow(this.lastActive);
26151 if(last !== false){
26155 this.selectFirstRow();
26157 this.fireEvent("afterselectionchange", this);
26161 this.grid.store.on('load', function(){
26162 this.selections.clear();
26165 var view = this.grid.view;
26166 view.on("refresh", this.onRefresh, this);
26167 view.on("rowupdated", this.onRowUpdated, this);
26168 view.on("rowremoved", this.onRemove, this);
26173 onRefresh : function()
26175 var ds = this.grid.store, i, v = this.grid.view;
26176 var s = this.selections;
26177 s.each(function(r){
26178 if((i = ds.indexOfId(r.id)) != -1){
26187 onRemove : function(v, index, r){
26188 this.selections.remove(r);
26192 onRowUpdated : function(v, index, r){
26193 if(this.isSelected(r)){
26194 v.onRowSelect(index);
26200 * @param {Array} records The records to select
26201 * @param {Boolean} keepExisting (optional) True to keep existing selections
26203 selectRecords : function(records, keepExisting)
26206 this.clearSelections();
26208 var ds = this.grid.store;
26209 for(var i = 0, len = records.length; i < len; i++){
26210 this.selectRow(ds.indexOf(records[i]), true);
26215 * Gets the number of selected rows.
26218 getCount : function(){
26219 return this.selections.length;
26223 * Selects the first row in the grid.
26225 selectFirstRow : function(){
26230 * Select the last row.
26231 * @param {Boolean} keepExisting (optional) True to keep existing selections
26233 selectLastRow : function(keepExisting){
26234 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26235 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26239 * Selects the row immediately following the last selected row.
26240 * @param {Boolean} keepExisting (optional) True to keep existing selections
26242 selectNext : function(keepExisting)
26244 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26245 this.selectRow(this.last+1, keepExisting);
26246 this.grid.getView().focusRow(this.last);
26251 * Selects the row that precedes the last selected row.
26252 * @param {Boolean} keepExisting (optional) True to keep existing selections
26254 selectPrevious : function(keepExisting){
26256 this.selectRow(this.last-1, keepExisting);
26257 this.grid.getView().focusRow(this.last);
26262 * Returns the selected records
26263 * @return {Array} Array of selected records
26265 getSelections : function(){
26266 return [].concat(this.selections.items);
26270 * Returns the first selected record.
26273 getSelected : function(){
26274 return this.selections.itemAt(0);
26279 * Clears all selections.
26281 clearSelections : function(fast)
26287 var ds = this.grid.store;
26288 var s = this.selections;
26289 s.each(function(r){
26290 this.deselectRow(ds.indexOfId(r.id));
26294 this.selections.clear();
26301 * Selects all rows.
26303 selectAll : function(){
26307 this.selections.clear();
26308 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26309 this.selectRow(i, true);
26314 * Returns True if there is a selection.
26315 * @return {Boolean}
26317 hasSelection : function(){
26318 return this.selections.length > 0;
26322 * Returns True if the specified row is selected.
26323 * @param {Number/Record} record The record or index of the record to check
26324 * @return {Boolean}
26326 isSelected : function(index){
26327 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26328 return (r && this.selections.key(r.id) ? true : false);
26332 * Returns True if the specified record id is selected.
26333 * @param {String} id The id of record to check
26334 * @return {Boolean}
26336 isIdSelected : function(id){
26337 return (this.selections.key(id) ? true : false);
26342 handleMouseDBClick : function(e, t){
26346 handleMouseDown : function(e, t)
26348 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26349 if(this.isLocked() || rowIndex < 0 ){
26352 if(e.shiftKey && this.last !== false){
26353 var last = this.last;
26354 this.selectRange(last, rowIndex, e.ctrlKey);
26355 this.last = last; // reset the last
26359 var isSelected = this.isSelected(rowIndex);
26360 //Roo.log("select row:" + rowIndex);
26362 this.deselectRow(rowIndex);
26364 this.selectRow(rowIndex, true);
26368 if(e.button !== 0 && isSelected){
26369 alert('rowIndex 2: ' + rowIndex);
26370 view.focusRow(rowIndex);
26371 }else if(e.ctrlKey && isSelected){
26372 this.deselectRow(rowIndex);
26373 }else if(!isSelected){
26374 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26375 view.focusRow(rowIndex);
26379 this.fireEvent("afterselectionchange", this);
26382 handleDragableRowClick : function(grid, rowIndex, e)
26384 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26385 this.selectRow(rowIndex, false);
26386 grid.view.focusRow(rowIndex);
26387 this.fireEvent("afterselectionchange", this);
26392 * Selects multiple rows.
26393 * @param {Array} rows Array of the indexes of the row to select
26394 * @param {Boolean} keepExisting (optional) True to keep existing selections
26396 selectRows : function(rows, keepExisting){
26398 this.clearSelections();
26400 for(var i = 0, len = rows.length; i < len; i++){
26401 this.selectRow(rows[i], true);
26406 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26407 * @param {Number} startRow The index of the first row in the range
26408 * @param {Number} endRow The index of the last row in the range
26409 * @param {Boolean} keepExisting (optional) True to retain existing selections
26411 selectRange : function(startRow, endRow, keepExisting){
26416 this.clearSelections();
26418 if(startRow <= endRow){
26419 for(var i = startRow; i <= endRow; i++){
26420 this.selectRow(i, true);
26423 for(var i = startRow; i >= endRow; i--){
26424 this.selectRow(i, true);
26430 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26431 * @param {Number} startRow The index of the first row in the range
26432 * @param {Number} endRow The index of the last row in the range
26434 deselectRange : function(startRow, endRow, preventViewNotify){
26438 for(var i = startRow; i <= endRow; i++){
26439 this.deselectRow(i, preventViewNotify);
26445 * @param {Number} row The index of the row to select
26446 * @param {Boolean} keepExisting (optional) True to keep existing selections
26448 selectRow : function(index, keepExisting, preventViewNotify)
26450 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26453 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26454 if(!keepExisting || this.singleSelect){
26455 this.clearSelections();
26458 var r = this.grid.store.getAt(index);
26459 //console.log('selectRow - record id :' + r.id);
26461 this.selections.add(r);
26462 this.last = this.lastActive = index;
26463 if(!preventViewNotify){
26464 var proxy = new Roo.Element(
26465 this.grid.getRowDom(index)
26467 proxy.addClass('bg-info info');
26469 this.fireEvent("rowselect", this, index, r);
26470 this.fireEvent("selectionchange", this);
26476 * @param {Number} row The index of the row to deselect
26478 deselectRow : function(index, preventViewNotify)
26483 if(this.last == index){
26486 if(this.lastActive == index){
26487 this.lastActive = false;
26490 var r = this.grid.store.getAt(index);
26495 this.selections.remove(r);
26496 //.console.log('deselectRow - record id :' + r.id);
26497 if(!preventViewNotify){
26499 var proxy = new Roo.Element(
26500 this.grid.getRowDom(index)
26502 proxy.removeClass('bg-info info');
26504 this.fireEvent("rowdeselect", this, index);
26505 this.fireEvent("selectionchange", this);
26509 restoreLast : function(){
26511 this.last = this._last;
26516 acceptsNav : function(row, col, cm){
26517 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26521 onEditorKey : function(field, e){
26522 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26527 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26529 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26531 }else if(k == e.ENTER && !e.ctrlKey){
26535 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26537 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26539 }else if(k == e.ESC){
26543 g.startEditing(newCell[0], newCell[1]);
26549 * Ext JS Library 1.1.1
26550 * Copyright(c) 2006-2007, Ext JS, LLC.
26552 * Originally Released Under LGPL - original licence link has changed is not relivant.
26555 * <script type="text/javascript">
26559 * @class Roo.bootstrap.PagingToolbar
26560 * @extends Roo.bootstrap.NavSimplebar
26561 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26563 * Create a new PagingToolbar
26564 * @param {Object} config The config object
26565 * @param {Roo.data.Store} store
26567 Roo.bootstrap.PagingToolbar = function(config)
26569 // old args format still supported... - xtype is prefered..
26570 // created from xtype...
26572 this.ds = config.dataSource;
26574 if (config.store && !this.ds) {
26575 this.store= Roo.factory(config.store, Roo.data);
26576 this.ds = this.store;
26577 this.ds.xmodule = this.xmodule || false;
26580 this.toolbarItems = [];
26581 if (config.items) {
26582 this.toolbarItems = config.items;
26585 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26590 this.bind(this.ds);
26593 if (Roo.bootstrap.version == 4) {
26594 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26596 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26601 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26603 * @cfg {Roo.data.Store} dataSource
26604 * The underlying data store providing the paged data
26607 * @cfg {String/HTMLElement/Element} container
26608 * container The id or element that will contain the toolbar
26611 * @cfg {Boolean} displayInfo
26612 * True to display the displayMsg (defaults to false)
26615 * @cfg {Number} pageSize
26616 * The number of records to display per page (defaults to 20)
26620 * @cfg {String} displayMsg
26621 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26623 displayMsg : 'Displaying {0} - {1} of {2}',
26625 * @cfg {String} emptyMsg
26626 * The message to display when no records are found (defaults to "No data to display")
26628 emptyMsg : 'No data to display',
26630 * Customizable piece of the default paging text (defaults to "Page")
26633 beforePageText : "Page",
26635 * Customizable piece of the default paging text (defaults to "of %0")
26638 afterPageText : "of {0}",
26640 * Customizable piece of the default paging text (defaults to "First Page")
26643 firstText : "First Page",
26645 * Customizable piece of the default paging text (defaults to "Previous Page")
26648 prevText : "Previous Page",
26650 * Customizable piece of the default paging text (defaults to "Next Page")
26653 nextText : "Next Page",
26655 * Customizable piece of the default paging text (defaults to "Last Page")
26658 lastText : "Last Page",
26660 * Customizable piece of the default paging text (defaults to "Refresh")
26663 refreshText : "Refresh",
26667 onRender : function(ct, position)
26669 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26670 this.navgroup.parentId = this.id;
26671 this.navgroup.onRender(this.el, null);
26672 // add the buttons to the navgroup
26674 if(this.displayInfo){
26675 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26676 this.displayEl = this.el.select('.x-paging-info', true).first();
26677 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26678 // this.displayEl = navel.el.select('span',true).first();
26684 Roo.each(_this.buttons, function(e){ // this might need to use render????
26685 Roo.factory(e).render(_this.el);
26689 Roo.each(_this.toolbarItems, function(e) {
26690 _this.navgroup.addItem(e);
26694 this.first = this.navgroup.addItem({
26695 tooltip: this.firstText,
26696 cls: "prev btn-outline-secondary",
26697 html : ' <i class="fa fa-step-backward"></i>',
26699 preventDefault: true,
26700 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26703 this.prev = this.navgroup.addItem({
26704 tooltip: this.prevText,
26705 cls: "prev btn-outline-secondary",
26706 html : ' <i class="fa fa-backward"></i>',
26708 preventDefault: true,
26709 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26711 //this.addSeparator();
26714 var field = this.navgroup.addItem( {
26716 cls : 'x-paging-position btn-outline-secondary',
26718 html : this.beforePageText +
26719 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26720 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26723 this.field = field.el.select('input', true).first();
26724 this.field.on("keydown", this.onPagingKeydown, this);
26725 this.field.on("focus", function(){this.dom.select();});
26728 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26729 //this.field.setHeight(18);
26730 //this.addSeparator();
26731 this.next = this.navgroup.addItem({
26732 tooltip: this.nextText,
26733 cls: "next btn-outline-secondary",
26734 html : ' <i class="fa fa-forward"></i>',
26736 preventDefault: true,
26737 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26739 this.last = this.navgroup.addItem({
26740 tooltip: this.lastText,
26741 html : ' <i class="fa fa-step-forward"></i>',
26742 cls: "next btn-outline-secondary",
26744 preventDefault: true,
26745 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26747 //this.addSeparator();
26748 this.loading = this.navgroup.addItem({
26749 tooltip: this.refreshText,
26750 cls: "btn-outline-secondary",
26751 html : ' <i class="fa fa-refresh"></i>',
26752 preventDefault: true,
26753 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26759 updateInfo : function(){
26760 if(this.displayEl){
26761 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26762 var msg = count == 0 ?
26766 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26768 this.displayEl.update(msg);
26773 onLoad : function(ds, r, o)
26775 this.cursor = o.params.start ? o.params.start : 0;
26777 var d = this.getPageData(),
26782 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26783 this.field.dom.value = ap;
26784 this.first.setDisabled(ap == 1);
26785 this.prev.setDisabled(ap == 1);
26786 this.next.setDisabled(ap == ps);
26787 this.last.setDisabled(ap == ps);
26788 this.loading.enable();
26793 getPageData : function(){
26794 var total = this.ds.getTotalCount();
26797 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26798 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26803 onLoadError : function(){
26804 this.loading.enable();
26808 onPagingKeydown : function(e){
26809 var k = e.getKey();
26810 var d = this.getPageData();
26812 var v = this.field.dom.value, pageNum;
26813 if(!v || isNaN(pageNum = parseInt(v, 10))){
26814 this.field.dom.value = d.activePage;
26817 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26818 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26821 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))
26823 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26824 this.field.dom.value = pageNum;
26825 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26828 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26830 var v = this.field.dom.value, pageNum;
26831 var increment = (e.shiftKey) ? 10 : 1;
26832 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26835 if(!v || isNaN(pageNum = parseInt(v, 10))) {
26836 this.field.dom.value = d.activePage;
26839 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26841 this.field.dom.value = parseInt(v, 10) + increment;
26842 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26843 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26850 beforeLoad : function(){
26852 this.loading.disable();
26857 onClick : function(which){
26866 ds.load({params:{start: 0, limit: this.pageSize}});
26869 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26872 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26875 var total = ds.getTotalCount();
26876 var extra = total % this.pageSize;
26877 var lastStart = extra ? (total - extra) : total-this.pageSize;
26878 ds.load({params:{start: lastStart, limit: this.pageSize}});
26881 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26887 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26888 * @param {Roo.data.Store} store The data store to unbind
26890 unbind : function(ds){
26891 ds.un("beforeload", this.beforeLoad, this);
26892 ds.un("load", this.onLoad, this);
26893 ds.un("loadexception", this.onLoadError, this);
26894 ds.un("remove", this.updateInfo, this);
26895 ds.un("add", this.updateInfo, this);
26896 this.ds = undefined;
26900 * Binds the paging toolbar to the specified {@link Roo.data.Store}
26901 * @param {Roo.data.Store} store The data store to bind
26903 bind : function(ds){
26904 ds.on("beforeload", this.beforeLoad, this);
26905 ds.on("load", this.onLoad, this);
26906 ds.on("loadexception", this.onLoadError, this);
26907 ds.on("remove", this.updateInfo, this);
26908 ds.on("add", this.updateInfo, this);
26919 * @class Roo.bootstrap.MessageBar
26920 * @extends Roo.bootstrap.Component
26921 * Bootstrap MessageBar class
26922 * @cfg {String} html contents of the MessageBar
26923 * @cfg {String} weight (info | success | warning | danger) default info
26924 * @cfg {String} beforeClass insert the bar before the given class
26925 * @cfg {Boolean} closable (true | false) default false
26926 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26929 * Create a new Element
26930 * @param {Object} config The config object
26933 Roo.bootstrap.MessageBar = function(config){
26934 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26937 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
26943 beforeClass: 'bootstrap-sticky-wrap',
26945 getAutoCreate : function(){
26949 cls: 'alert alert-dismissable alert-' + this.weight,
26954 html: this.html || ''
26960 cfg.cls += ' alert-messages-fixed';
26974 onRender : function(ct, position)
26976 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26979 var cfg = Roo.apply({}, this.getAutoCreate());
26983 cfg.cls += ' ' + this.cls;
26986 cfg.style = this.style;
26988 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26990 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26993 this.el.select('>button.close').on('click', this.hide, this);
26999 if (!this.rendered) {
27005 this.fireEvent('show', this);
27011 if (!this.rendered) {
27017 this.fireEvent('hide', this);
27020 update : function()
27022 // var e = this.el.dom.firstChild;
27024 // if(this.closable){
27025 // e = e.nextSibling;
27028 // e.data = this.html || '';
27030 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27046 * @class Roo.bootstrap.Graph
27047 * @extends Roo.bootstrap.Component
27048 * Bootstrap Graph class
27052 @cfg {String} graphtype bar | vbar | pie
27053 @cfg {number} g_x coodinator | centre x (pie)
27054 @cfg {number} g_y coodinator | centre y (pie)
27055 @cfg {number} g_r radius (pie)
27056 @cfg {number} g_height height of the chart (respected by all elements in the set)
27057 @cfg {number} g_width width of the chart (respected by all elements in the set)
27058 @cfg {Object} title The title of the chart
27061 -opts (object) options for the chart
27063 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27064 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27066 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.
27067 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27069 o stretch (boolean)
27071 -opts (object) options for the pie
27074 o startAngle (number)
27075 o endAngle (number)
27079 * Create a new Input
27080 * @param {Object} config The config object
27083 Roo.bootstrap.Graph = function(config){
27084 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27090 * The img click event for the img.
27091 * @param {Roo.EventObject} e
27097 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27108 //g_colors: this.colors,
27115 getAutoCreate : function(){
27126 onRender : function(ct,position){
27129 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27131 if (typeof(Raphael) == 'undefined') {
27132 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27136 this.raphael = Raphael(this.el.dom);
27138 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27139 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27140 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27141 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27143 r.text(160, 10, "Single Series Chart").attr(txtattr);
27144 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27145 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27146 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27148 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27149 r.barchart(330, 10, 300, 220, data1);
27150 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27151 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27154 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27155 // r.barchart(30, 30, 560, 250, xdata, {
27156 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27157 // axis : "0 0 1 1",
27158 // axisxlabels : xdata
27159 // //yvalues : cols,
27162 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27164 // this.load(null,xdata,{
27165 // axis : "0 0 1 1",
27166 // axisxlabels : xdata
27171 load : function(graphtype,xdata,opts)
27173 this.raphael.clear();
27175 graphtype = this.graphtype;
27180 var r = this.raphael,
27181 fin = function () {
27182 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27184 fout = function () {
27185 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27187 pfin = function() {
27188 this.sector.stop();
27189 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27192 this.label[0].stop();
27193 this.label[0].attr({ r: 7.5 });
27194 this.label[1].attr({ "font-weight": 800 });
27197 pfout = function() {
27198 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27201 this.label[0].animate({ r: 5 }, 500, "bounce");
27202 this.label[1].attr({ "font-weight": 400 });
27208 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27211 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27214 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27215 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27217 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27224 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27229 setTitle: function(o)
27234 initEvents: function() {
27237 this.el.on('click', this.onClick, this);
27241 onClick : function(e)
27243 Roo.log('img onclick');
27244 this.fireEvent('click', this, e);
27256 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27259 * @class Roo.bootstrap.dash.NumberBox
27260 * @extends Roo.bootstrap.Component
27261 * Bootstrap NumberBox class
27262 * @cfg {String} headline Box headline
27263 * @cfg {String} content Box content
27264 * @cfg {String} icon Box icon
27265 * @cfg {String} footer Footer text
27266 * @cfg {String} fhref Footer href
27269 * Create a new NumberBox
27270 * @param {Object} config The config object
27274 Roo.bootstrap.dash.NumberBox = function(config){
27275 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27279 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27288 getAutoCreate : function(){
27292 cls : 'small-box ',
27300 cls : 'roo-headline',
27301 html : this.headline
27305 cls : 'roo-content',
27306 html : this.content
27320 cls : 'ion ' + this.icon
27329 cls : 'small-box-footer',
27330 href : this.fhref || '#',
27334 cfg.cn.push(footer);
27341 onRender : function(ct,position){
27342 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27349 setHeadline: function (value)
27351 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27354 setFooter: function (value, href)
27356 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27359 this.el.select('a.small-box-footer',true).first().attr('href', href);
27364 setContent: function (value)
27366 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27369 initEvents: function()
27383 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27386 * @class Roo.bootstrap.dash.TabBox
27387 * @extends Roo.bootstrap.Component
27388 * Bootstrap TabBox class
27389 * @cfg {String} title Title of the TabBox
27390 * @cfg {String} icon Icon of the TabBox
27391 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27392 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27395 * Create a new TabBox
27396 * @param {Object} config The config object
27400 Roo.bootstrap.dash.TabBox = function(config){
27401 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27406 * When a pane is added
27407 * @param {Roo.bootstrap.dash.TabPane} pane
27411 * @event activatepane
27412 * When a pane is activated
27413 * @param {Roo.bootstrap.dash.TabPane} pane
27415 "activatepane" : true
27423 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27428 tabScrollable : false,
27430 getChildContainer : function()
27432 return this.el.select('.tab-content', true).first();
27435 getAutoCreate : function(){
27439 cls: 'pull-left header',
27447 cls: 'fa ' + this.icon
27453 cls: 'nav nav-tabs pull-right',
27459 if(this.tabScrollable){
27466 cls: 'nav nav-tabs pull-right',
27477 cls: 'nav-tabs-custom',
27482 cls: 'tab-content no-padding',
27490 initEvents : function()
27492 //Roo.log('add add pane handler');
27493 this.on('addpane', this.onAddPane, this);
27496 * Updates the box title
27497 * @param {String} html to set the title to.
27499 setTitle : function(value)
27501 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27503 onAddPane : function(pane)
27505 this.panes.push(pane);
27506 //Roo.log('addpane');
27508 // tabs are rendere left to right..
27509 if(!this.showtabs){
27513 var ctr = this.el.select('.nav-tabs', true).first();
27516 var existing = ctr.select('.nav-tab',true);
27517 var qty = existing.getCount();;
27520 var tab = ctr.createChild({
27522 cls : 'nav-tab' + (qty ? '' : ' active'),
27530 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27533 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27535 pane.el.addClass('active');
27540 onTabClick : function(ev,un,ob,pane)
27542 //Roo.log('tab - prev default');
27543 ev.preventDefault();
27546 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27547 pane.tab.addClass('active');
27548 //Roo.log(pane.title);
27549 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27550 // technically we should have a deactivate event.. but maybe add later.
27551 // and it should not de-activate the selected tab...
27552 this.fireEvent('activatepane', pane);
27553 pane.el.addClass('active');
27554 pane.fireEvent('activate');
27559 getActivePane : function()
27562 Roo.each(this.panes, function(p) {
27563 if(p.el.hasClass('active')){
27584 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27586 * @class Roo.bootstrap.TabPane
27587 * @extends Roo.bootstrap.Component
27588 * Bootstrap TabPane class
27589 * @cfg {Boolean} active (false | true) Default false
27590 * @cfg {String} title title of panel
27594 * Create a new TabPane
27595 * @param {Object} config The config object
27598 Roo.bootstrap.dash.TabPane = function(config){
27599 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27605 * When a pane is activated
27606 * @param {Roo.bootstrap.dash.TabPane} pane
27613 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27618 // the tabBox that this is attached to.
27621 getAutoCreate : function()
27629 cfg.cls += ' active';
27634 initEvents : function()
27636 //Roo.log('trigger add pane handler');
27637 this.parent().fireEvent('addpane', this)
27641 * Updates the tab title
27642 * @param {String} html to set the title to.
27644 setTitle: function(str)
27650 this.tab.select('a', true).first().dom.innerHTML = str;
27667 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27670 * @class Roo.bootstrap.menu.Menu
27671 * @extends Roo.bootstrap.Component
27672 * Bootstrap Menu class - container for Menu
27673 * @cfg {String} html Text of the menu
27674 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27675 * @cfg {String} icon Font awesome icon
27676 * @cfg {String} pos Menu align to (top | bottom) default bottom
27680 * Create a new Menu
27681 * @param {Object} config The config object
27685 Roo.bootstrap.menu.Menu = function(config){
27686 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27690 * @event beforeshow
27691 * Fires before this menu is displayed
27692 * @param {Roo.bootstrap.menu.Menu} this
27696 * @event beforehide
27697 * Fires before this menu is hidden
27698 * @param {Roo.bootstrap.menu.Menu} this
27703 * Fires after this menu is displayed
27704 * @param {Roo.bootstrap.menu.Menu} this
27709 * Fires after this menu is hidden
27710 * @param {Roo.bootstrap.menu.Menu} this
27715 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27716 * @param {Roo.bootstrap.menu.Menu} this
27717 * @param {Roo.EventObject} e
27724 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27728 weight : 'default',
27733 getChildContainer : function() {
27734 if(this.isSubMenu){
27738 return this.el.select('ul.dropdown-menu', true).first();
27741 getAutoCreate : function()
27746 cls : 'roo-menu-text',
27754 cls : 'fa ' + this.icon
27765 cls : 'dropdown-button btn btn-' + this.weight,
27770 cls : 'dropdown-toggle btn btn-' + this.weight,
27780 cls : 'dropdown-menu'
27786 if(this.pos == 'top'){
27787 cfg.cls += ' dropup';
27790 if(this.isSubMenu){
27793 cls : 'dropdown-menu'
27800 onRender : function(ct, position)
27802 this.isSubMenu = ct.hasClass('dropdown-submenu');
27804 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27807 initEvents : function()
27809 if(this.isSubMenu){
27813 this.hidden = true;
27815 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27816 this.triggerEl.on('click', this.onTriggerPress, this);
27818 this.buttonEl = this.el.select('button.dropdown-button', true).first();
27819 this.buttonEl.on('click', this.onClick, this);
27825 if(this.isSubMenu){
27829 return this.el.select('ul.dropdown-menu', true).first();
27832 onClick : function(e)
27834 this.fireEvent("click", this, e);
27837 onTriggerPress : function(e)
27839 if (this.isVisible()) {
27846 isVisible : function(){
27847 return !this.hidden;
27852 this.fireEvent("beforeshow", this);
27854 this.hidden = false;
27855 this.el.addClass('open');
27857 Roo.get(document).on("mouseup", this.onMouseUp, this);
27859 this.fireEvent("show", this);
27866 this.fireEvent("beforehide", this);
27868 this.hidden = true;
27869 this.el.removeClass('open');
27871 Roo.get(document).un("mouseup", this.onMouseUp);
27873 this.fireEvent("hide", this);
27876 onMouseUp : function()
27890 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27893 * @class Roo.bootstrap.menu.Item
27894 * @extends Roo.bootstrap.Component
27895 * Bootstrap MenuItem class
27896 * @cfg {Boolean} submenu (true | false) default false
27897 * @cfg {String} html text of the item
27898 * @cfg {String} href the link
27899 * @cfg {Boolean} disable (true | false) default false
27900 * @cfg {Boolean} preventDefault (true | false) default true
27901 * @cfg {String} icon Font awesome icon
27902 * @cfg {String} pos Submenu align to (left | right) default right
27906 * Create a new Item
27907 * @param {Object} config The config object
27911 Roo.bootstrap.menu.Item = function(config){
27912 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27916 * Fires when the mouse is hovering over this menu
27917 * @param {Roo.bootstrap.menu.Item} this
27918 * @param {Roo.EventObject} e
27923 * Fires when the mouse exits this menu
27924 * @param {Roo.bootstrap.menu.Item} this
27925 * @param {Roo.EventObject} e
27931 * The raw click event for the entire grid.
27932 * @param {Roo.EventObject} e
27938 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
27943 preventDefault: true,
27948 getAutoCreate : function()
27953 cls : 'roo-menu-item-text',
27961 cls : 'fa ' + this.icon
27970 href : this.href || '#',
27977 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27981 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27983 if(this.pos == 'left'){
27984 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27991 initEvents : function()
27993 this.el.on('mouseover', this.onMouseOver, this);
27994 this.el.on('mouseout', this.onMouseOut, this);
27996 this.el.select('a', true).first().on('click', this.onClick, this);
28000 onClick : function(e)
28002 if(this.preventDefault){
28003 e.preventDefault();
28006 this.fireEvent("click", this, e);
28009 onMouseOver : function(e)
28011 if(this.submenu && this.pos == 'left'){
28012 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28015 this.fireEvent("mouseover", this, e);
28018 onMouseOut : function(e)
28020 this.fireEvent("mouseout", this, e);
28032 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28035 * @class Roo.bootstrap.menu.Separator
28036 * @extends Roo.bootstrap.Component
28037 * Bootstrap Separator class
28040 * Create a new Separator
28041 * @param {Object} config The config object
28045 Roo.bootstrap.menu.Separator = function(config){
28046 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28049 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28051 getAutoCreate : function(){
28072 * @class Roo.bootstrap.Tooltip
28073 * Bootstrap Tooltip class
28074 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28075 * to determine which dom element triggers the tooltip.
28077 * It needs to add support for additional attributes like tooltip-position
28080 * Create a new Toolti
28081 * @param {Object} config The config object
28084 Roo.bootstrap.Tooltip = function(config){
28085 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28087 this.alignment = Roo.bootstrap.Tooltip.alignment;
28089 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28090 this.alignment = config.alignment;
28095 Roo.apply(Roo.bootstrap.Tooltip, {
28097 * @function init initialize tooltip monitoring.
28101 currentTip : false,
28102 currentRegion : false,
28108 Roo.get(document).on('mouseover', this.enter ,this);
28109 Roo.get(document).on('mouseout', this.leave, this);
28112 this.currentTip = new Roo.bootstrap.Tooltip();
28115 enter : function(ev)
28117 var dom = ev.getTarget();
28119 //Roo.log(['enter',dom]);
28120 var el = Roo.fly(dom);
28121 if (this.currentEl) {
28123 //Roo.log(this.currentEl);
28124 //Roo.log(this.currentEl.contains(dom));
28125 if (this.currentEl == el) {
28128 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28134 if (this.currentTip.el) {
28135 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28139 if(!el || el.dom == document){
28145 // you can not look for children, as if el is the body.. then everythign is the child..
28146 if (!el.attr('tooltip')) { //
28147 if (!el.select("[tooltip]").elements.length) {
28150 // is the mouse over this child...?
28151 bindEl = el.select("[tooltip]").first();
28152 var xy = ev.getXY();
28153 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28154 //Roo.log("not in region.");
28157 //Roo.log("child element over..");
28160 this.currentEl = bindEl;
28161 this.currentTip.bind(bindEl);
28162 this.currentRegion = Roo.lib.Region.getRegion(dom);
28163 this.currentTip.enter();
28166 leave : function(ev)
28168 var dom = ev.getTarget();
28169 //Roo.log(['leave',dom]);
28170 if (!this.currentEl) {
28175 if (dom != this.currentEl.dom) {
28178 var xy = ev.getXY();
28179 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28182 // only activate leave if mouse cursor is outside... bounding box..
28187 if (this.currentTip) {
28188 this.currentTip.leave();
28190 //Roo.log('clear currentEl');
28191 this.currentEl = false;
28196 'left' : ['r-l', [-2,0], 'right'],
28197 'right' : ['l-r', [2,0], 'left'],
28198 'bottom' : ['t-b', [0,2], 'top'],
28199 'top' : [ 'b-t', [0,-2], 'bottom']
28205 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28210 delay : null, // can be { show : 300 , hide: 500}
28214 hoverState : null, //???
28216 placement : 'bottom',
28220 getAutoCreate : function(){
28227 cls : 'tooltip-arrow'
28230 cls : 'tooltip-inner'
28237 bind : function(el)
28243 enter : function () {
28245 if (this.timeout != null) {
28246 clearTimeout(this.timeout);
28249 this.hoverState = 'in';
28250 //Roo.log("enter - show");
28251 if (!this.delay || !this.delay.show) {
28256 this.timeout = setTimeout(function () {
28257 if (_t.hoverState == 'in') {
28260 }, this.delay.show);
28264 clearTimeout(this.timeout);
28266 this.hoverState = 'out';
28267 if (!this.delay || !this.delay.hide) {
28273 this.timeout = setTimeout(function () {
28274 //Roo.log("leave - timeout");
28276 if (_t.hoverState == 'out') {
28278 Roo.bootstrap.Tooltip.currentEl = false;
28283 show : function (msg)
28286 this.render(document.body);
28289 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28291 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28293 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28295 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28297 var placement = typeof this.placement == 'function' ?
28298 this.placement.call(this, this.el, on_el) :
28301 var autoToken = /\s?auto?\s?/i;
28302 var autoPlace = autoToken.test(placement);
28304 placement = placement.replace(autoToken, '') || 'top';
28308 //this.el.setXY([0,0]);
28310 //this.el.dom.style.display='block';
28312 //this.el.appendTo(on_el);
28314 var p = this.getPosition();
28315 var box = this.el.getBox();
28321 var align = this.alignment[placement];
28323 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28325 if(placement == 'top' || placement == 'bottom'){
28327 placement = 'right';
28330 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28331 placement = 'left';
28334 var scroll = Roo.select('body', true).first().getScroll();
28336 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28340 align = this.alignment[placement];
28343 this.el.alignTo(this.bindEl, align[0],align[1]);
28344 //var arrow = this.el.select('.arrow',true).first();
28345 //arrow.set(align[2],
28347 this.el.addClass(placement);
28349 this.el.addClass('in fade');
28351 this.hoverState = null;
28353 if (this.el.hasClass('fade')) {
28364 //this.el.setXY([0,0]);
28365 this.el.removeClass('in');
28381 * @class Roo.bootstrap.LocationPicker
28382 * @extends Roo.bootstrap.Component
28383 * Bootstrap LocationPicker class
28384 * @cfg {Number} latitude Position when init default 0
28385 * @cfg {Number} longitude Position when init default 0
28386 * @cfg {Number} zoom default 15
28387 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28388 * @cfg {Boolean} mapTypeControl default false
28389 * @cfg {Boolean} disableDoubleClickZoom default false
28390 * @cfg {Boolean} scrollwheel default true
28391 * @cfg {Boolean} streetViewControl default false
28392 * @cfg {Number} radius default 0
28393 * @cfg {String} locationName
28394 * @cfg {Boolean} draggable default true
28395 * @cfg {Boolean} enableAutocomplete default false
28396 * @cfg {Boolean} enableReverseGeocode default true
28397 * @cfg {String} markerTitle
28400 * Create a new LocationPicker
28401 * @param {Object} config The config object
28405 Roo.bootstrap.LocationPicker = function(config){
28407 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28412 * Fires when the picker initialized.
28413 * @param {Roo.bootstrap.LocationPicker} this
28414 * @param {Google Location} location
28418 * @event positionchanged
28419 * Fires when the picker position changed.
28420 * @param {Roo.bootstrap.LocationPicker} this
28421 * @param {Google Location} location
28423 positionchanged : true,
28426 * Fires when the map resize.
28427 * @param {Roo.bootstrap.LocationPicker} this
28432 * Fires when the map show.
28433 * @param {Roo.bootstrap.LocationPicker} this
28438 * Fires when the map hide.
28439 * @param {Roo.bootstrap.LocationPicker} this
28444 * Fires when click the map.
28445 * @param {Roo.bootstrap.LocationPicker} this
28446 * @param {Map event} e
28450 * @event mapRightClick
28451 * Fires when right click the map.
28452 * @param {Roo.bootstrap.LocationPicker} this
28453 * @param {Map event} e
28455 mapRightClick : true,
28457 * @event markerClick
28458 * Fires when click the marker.
28459 * @param {Roo.bootstrap.LocationPicker} this
28460 * @param {Map event} e
28462 markerClick : true,
28464 * @event markerRightClick
28465 * Fires when right click the marker.
28466 * @param {Roo.bootstrap.LocationPicker} this
28467 * @param {Map event} e
28469 markerRightClick : true,
28471 * @event OverlayViewDraw
28472 * Fires when OverlayView Draw
28473 * @param {Roo.bootstrap.LocationPicker} this
28475 OverlayViewDraw : true,
28477 * @event OverlayViewOnAdd
28478 * Fires when OverlayView Draw
28479 * @param {Roo.bootstrap.LocationPicker} this
28481 OverlayViewOnAdd : true,
28483 * @event OverlayViewOnRemove
28484 * Fires when OverlayView Draw
28485 * @param {Roo.bootstrap.LocationPicker} this
28487 OverlayViewOnRemove : true,
28489 * @event OverlayViewShow
28490 * Fires when OverlayView Draw
28491 * @param {Roo.bootstrap.LocationPicker} this
28492 * @param {Pixel} cpx
28494 OverlayViewShow : true,
28496 * @event OverlayViewHide
28497 * Fires when OverlayView Draw
28498 * @param {Roo.bootstrap.LocationPicker} this
28500 OverlayViewHide : true,
28502 * @event loadexception
28503 * Fires when load google lib failed.
28504 * @param {Roo.bootstrap.LocationPicker} this
28506 loadexception : true
28511 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28513 gMapContext: false,
28519 mapTypeControl: false,
28520 disableDoubleClickZoom: false,
28522 streetViewControl: false,
28526 enableAutocomplete: false,
28527 enableReverseGeocode: true,
28530 getAutoCreate: function()
28535 cls: 'roo-location-picker'
28541 initEvents: function(ct, position)
28543 if(!this.el.getWidth() || this.isApplied()){
28547 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28552 initial: function()
28554 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28555 this.fireEvent('loadexception', this);
28559 if(!this.mapTypeId){
28560 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28563 this.gMapContext = this.GMapContext();
28565 this.initOverlayView();
28567 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28571 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28572 _this.setPosition(_this.gMapContext.marker.position);
28575 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28576 _this.fireEvent('mapClick', this, event);
28580 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28581 _this.fireEvent('mapRightClick', this, event);
28585 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28586 _this.fireEvent('markerClick', this, event);
28590 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28591 _this.fireEvent('markerRightClick', this, event);
28595 this.setPosition(this.gMapContext.location);
28597 this.fireEvent('initial', this, this.gMapContext.location);
28600 initOverlayView: function()
28604 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28608 _this.fireEvent('OverlayViewDraw', _this);
28613 _this.fireEvent('OverlayViewOnAdd', _this);
28616 onRemove: function()
28618 _this.fireEvent('OverlayViewOnRemove', _this);
28621 show: function(cpx)
28623 _this.fireEvent('OverlayViewShow', _this, cpx);
28628 _this.fireEvent('OverlayViewHide', _this);
28634 fromLatLngToContainerPixel: function(event)
28636 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28639 isApplied: function()
28641 return this.getGmapContext() == false ? false : true;
28644 getGmapContext: function()
28646 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28649 GMapContext: function()
28651 var position = new google.maps.LatLng(this.latitude, this.longitude);
28653 var _map = new google.maps.Map(this.el.dom, {
28656 mapTypeId: this.mapTypeId,
28657 mapTypeControl: this.mapTypeControl,
28658 disableDoubleClickZoom: this.disableDoubleClickZoom,
28659 scrollwheel: this.scrollwheel,
28660 streetViewControl: this.streetViewControl,
28661 locationName: this.locationName,
28662 draggable: this.draggable,
28663 enableAutocomplete: this.enableAutocomplete,
28664 enableReverseGeocode: this.enableReverseGeocode
28667 var _marker = new google.maps.Marker({
28668 position: position,
28670 title: this.markerTitle,
28671 draggable: this.draggable
28678 location: position,
28679 radius: this.radius,
28680 locationName: this.locationName,
28681 addressComponents: {
28682 formatted_address: null,
28683 addressLine1: null,
28684 addressLine2: null,
28686 streetNumber: null,
28690 stateOrProvince: null
28693 domContainer: this.el.dom,
28694 geodecoder: new google.maps.Geocoder()
28698 drawCircle: function(center, radius, options)
28700 if (this.gMapContext.circle != null) {
28701 this.gMapContext.circle.setMap(null);
28705 options = Roo.apply({}, options, {
28706 strokeColor: "#0000FF",
28707 strokeOpacity: .35,
28709 fillColor: "#0000FF",
28713 options.map = this.gMapContext.map;
28714 options.radius = radius;
28715 options.center = center;
28716 this.gMapContext.circle = new google.maps.Circle(options);
28717 return this.gMapContext.circle;
28723 setPosition: function(location)
28725 this.gMapContext.location = location;
28726 this.gMapContext.marker.setPosition(location);
28727 this.gMapContext.map.panTo(location);
28728 this.drawCircle(location, this.gMapContext.radius, {});
28732 if (this.gMapContext.settings.enableReverseGeocode) {
28733 this.gMapContext.geodecoder.geocode({
28734 latLng: this.gMapContext.location
28735 }, function(results, status) {
28737 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28738 _this.gMapContext.locationName = results[0].formatted_address;
28739 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28741 _this.fireEvent('positionchanged', this, location);
28748 this.fireEvent('positionchanged', this, location);
28753 google.maps.event.trigger(this.gMapContext.map, "resize");
28755 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28757 this.fireEvent('resize', this);
28760 setPositionByLatLng: function(latitude, longitude)
28762 this.setPosition(new google.maps.LatLng(latitude, longitude));
28765 getCurrentPosition: function()
28768 latitude: this.gMapContext.location.lat(),
28769 longitude: this.gMapContext.location.lng()
28773 getAddressName: function()
28775 return this.gMapContext.locationName;
28778 getAddressComponents: function()
28780 return this.gMapContext.addressComponents;
28783 address_component_from_google_geocode: function(address_components)
28787 for (var i = 0; i < address_components.length; i++) {
28788 var component = address_components[i];
28789 if (component.types.indexOf("postal_code") >= 0) {
28790 result.postalCode = component.short_name;
28791 } else if (component.types.indexOf("street_number") >= 0) {
28792 result.streetNumber = component.short_name;
28793 } else if (component.types.indexOf("route") >= 0) {
28794 result.streetName = component.short_name;
28795 } else if (component.types.indexOf("neighborhood") >= 0) {
28796 result.city = component.short_name;
28797 } else if (component.types.indexOf("locality") >= 0) {
28798 result.city = component.short_name;
28799 } else if (component.types.indexOf("sublocality") >= 0) {
28800 result.district = component.short_name;
28801 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28802 result.stateOrProvince = component.short_name;
28803 } else if (component.types.indexOf("country") >= 0) {
28804 result.country = component.short_name;
28808 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28809 result.addressLine2 = "";
28813 setZoomLevel: function(zoom)
28815 this.gMapContext.map.setZoom(zoom);
28828 this.fireEvent('show', this);
28839 this.fireEvent('hide', this);
28844 Roo.apply(Roo.bootstrap.LocationPicker, {
28846 OverlayView : function(map, options)
28848 options = options || {};
28855 * @class Roo.bootstrap.Alert
28856 * @extends Roo.bootstrap.Component
28857 * Bootstrap Alert class - shows an alert area box
28859 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28860 Enter a valid email address
28863 * @cfg {String} title The title of alert
28864 * @cfg {String} html The content of alert
28865 * @cfg {String} weight ( success | info | warning | danger )
28866 * @cfg {String} faicon font-awesomeicon
28869 * Create a new alert
28870 * @param {Object} config The config object
28874 Roo.bootstrap.Alert = function(config){
28875 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28879 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
28886 getAutoCreate : function()
28895 cls : 'roo-alert-icon'
28900 cls : 'roo-alert-title',
28905 cls : 'roo-alert-text',
28912 cfg.cn[0].cls += ' fa ' + this.faicon;
28916 cfg.cls += ' alert-' + this.weight;
28922 initEvents: function()
28924 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28927 setTitle : function(str)
28929 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28932 setText : function(str)
28934 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28937 setWeight : function(weight)
28940 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28943 this.weight = weight;
28945 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28948 setIcon : function(icon)
28951 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28954 this.faicon = icon;
28956 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28977 * @class Roo.bootstrap.UploadCropbox
28978 * @extends Roo.bootstrap.Component
28979 * Bootstrap UploadCropbox class
28980 * @cfg {String} emptyText show when image has been loaded
28981 * @cfg {String} rotateNotify show when image too small to rotate
28982 * @cfg {Number} errorTimeout default 3000
28983 * @cfg {Number} minWidth default 300
28984 * @cfg {Number} minHeight default 300
28985 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28986 * @cfg {Boolean} isDocument (true|false) default false
28987 * @cfg {String} url action url
28988 * @cfg {String} paramName default 'imageUpload'
28989 * @cfg {String} method default POST
28990 * @cfg {Boolean} loadMask (true|false) default true
28991 * @cfg {Boolean} loadingText default 'Loading...'
28994 * Create a new UploadCropbox
28995 * @param {Object} config The config object
28998 Roo.bootstrap.UploadCropbox = function(config){
28999 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29003 * @event beforeselectfile
29004 * Fire before select file
29005 * @param {Roo.bootstrap.UploadCropbox} this
29007 "beforeselectfile" : true,
29010 * Fire after initEvent
29011 * @param {Roo.bootstrap.UploadCropbox} this
29016 * Fire after initEvent
29017 * @param {Roo.bootstrap.UploadCropbox} this
29018 * @param {String} data
29023 * Fire when preparing the file data
29024 * @param {Roo.bootstrap.UploadCropbox} this
29025 * @param {Object} file
29030 * Fire when get exception
29031 * @param {Roo.bootstrap.UploadCropbox} this
29032 * @param {XMLHttpRequest} xhr
29034 "exception" : true,
29036 * @event beforeloadcanvas
29037 * Fire before load the canvas
29038 * @param {Roo.bootstrap.UploadCropbox} this
29039 * @param {String} src
29041 "beforeloadcanvas" : true,
29044 * Fire when trash image
29045 * @param {Roo.bootstrap.UploadCropbox} this
29050 * Fire when download the image
29051 * @param {Roo.bootstrap.UploadCropbox} this
29055 * @event footerbuttonclick
29056 * Fire when footerbuttonclick
29057 * @param {Roo.bootstrap.UploadCropbox} this
29058 * @param {String} type
29060 "footerbuttonclick" : true,
29064 * @param {Roo.bootstrap.UploadCropbox} this
29069 * Fire when rotate the image
29070 * @param {Roo.bootstrap.UploadCropbox} this
29071 * @param {String} pos
29076 * Fire when inspect the file
29077 * @param {Roo.bootstrap.UploadCropbox} this
29078 * @param {Object} file
29083 * Fire when xhr upload the file
29084 * @param {Roo.bootstrap.UploadCropbox} this
29085 * @param {Object} data
29090 * Fire when arrange the file data
29091 * @param {Roo.bootstrap.UploadCropbox} this
29092 * @param {Object} formData
29097 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29100 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29102 emptyText : 'Click to upload image',
29103 rotateNotify : 'Image is too small to rotate',
29104 errorTimeout : 3000,
29118 cropType : 'image/jpeg',
29120 canvasLoaded : false,
29121 isDocument : false,
29123 paramName : 'imageUpload',
29125 loadingText : 'Loading...',
29128 getAutoCreate : function()
29132 cls : 'roo-upload-cropbox',
29136 cls : 'roo-upload-cropbox-selector',
29141 cls : 'roo-upload-cropbox-body',
29142 style : 'cursor:pointer',
29146 cls : 'roo-upload-cropbox-preview'
29150 cls : 'roo-upload-cropbox-thumb'
29154 cls : 'roo-upload-cropbox-empty-notify',
29155 html : this.emptyText
29159 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29160 html : this.rotateNotify
29166 cls : 'roo-upload-cropbox-footer',
29169 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29179 onRender : function(ct, position)
29181 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29183 if (this.buttons.length) {
29185 Roo.each(this.buttons, function(bb) {
29187 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29189 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29195 this.maskEl = this.el;
29199 initEvents : function()
29201 this.urlAPI = (window.createObjectURL && window) ||
29202 (window.URL && URL.revokeObjectURL && URL) ||
29203 (window.webkitURL && webkitURL);
29205 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29206 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29208 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29209 this.selectorEl.hide();
29211 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29212 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29214 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29215 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29216 this.thumbEl.hide();
29218 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29219 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29221 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29222 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29223 this.errorEl.hide();
29225 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29226 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29227 this.footerEl.hide();
29229 this.setThumbBoxSize();
29235 this.fireEvent('initial', this);
29242 window.addEventListener("resize", function() { _this.resize(); } );
29244 this.bodyEl.on('click', this.beforeSelectFile, this);
29247 this.bodyEl.on('touchstart', this.onTouchStart, this);
29248 this.bodyEl.on('touchmove', this.onTouchMove, this);
29249 this.bodyEl.on('touchend', this.onTouchEnd, this);
29253 this.bodyEl.on('mousedown', this.onMouseDown, this);
29254 this.bodyEl.on('mousemove', this.onMouseMove, this);
29255 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29256 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29257 Roo.get(document).on('mouseup', this.onMouseUp, this);
29260 this.selectorEl.on('change', this.onFileSelected, this);
29266 this.baseScale = 1;
29268 this.baseRotate = 1;
29269 this.dragable = false;
29270 this.pinching = false;
29273 this.cropData = false;
29274 this.notifyEl.dom.innerHTML = this.emptyText;
29276 this.selectorEl.dom.value = '';
29280 resize : function()
29282 if(this.fireEvent('resize', this) != false){
29283 this.setThumbBoxPosition();
29284 this.setCanvasPosition();
29288 onFooterButtonClick : function(e, el, o, type)
29291 case 'rotate-left' :
29292 this.onRotateLeft(e);
29294 case 'rotate-right' :
29295 this.onRotateRight(e);
29298 this.beforeSelectFile(e);
29313 this.fireEvent('footerbuttonclick', this, type);
29316 beforeSelectFile : function(e)
29318 e.preventDefault();
29320 if(this.fireEvent('beforeselectfile', this) != false){
29321 this.selectorEl.dom.click();
29325 onFileSelected : function(e)
29327 e.preventDefault();
29329 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29333 var file = this.selectorEl.dom.files[0];
29335 if(this.fireEvent('inspect', this, file) != false){
29336 this.prepare(file);
29341 trash : function(e)
29343 this.fireEvent('trash', this);
29346 download : function(e)
29348 this.fireEvent('download', this);
29351 loadCanvas : function(src)
29353 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29357 this.imageEl = document.createElement('img');
29361 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29363 this.imageEl.src = src;
29367 onLoadCanvas : function()
29369 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29370 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29372 this.bodyEl.un('click', this.beforeSelectFile, this);
29374 this.notifyEl.hide();
29375 this.thumbEl.show();
29376 this.footerEl.show();
29378 this.baseRotateLevel();
29380 if(this.isDocument){
29381 this.setThumbBoxSize();
29384 this.setThumbBoxPosition();
29386 this.baseScaleLevel();
29392 this.canvasLoaded = true;
29395 this.maskEl.unmask();
29400 setCanvasPosition : function()
29402 if(!this.canvasEl){
29406 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29407 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29409 this.previewEl.setLeft(pw);
29410 this.previewEl.setTop(ph);
29414 onMouseDown : function(e)
29418 this.dragable = true;
29419 this.pinching = false;
29421 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29422 this.dragable = false;
29426 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29427 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29431 onMouseMove : function(e)
29435 if(!this.canvasLoaded){
29439 if (!this.dragable){
29443 var minX = Math.ceil(this.thumbEl.getLeft(true));
29444 var minY = Math.ceil(this.thumbEl.getTop(true));
29446 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29447 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29449 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29450 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29452 x = x - this.mouseX;
29453 y = y - this.mouseY;
29455 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29456 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29458 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29459 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29461 this.previewEl.setLeft(bgX);
29462 this.previewEl.setTop(bgY);
29464 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29465 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29468 onMouseUp : function(e)
29472 this.dragable = false;
29475 onMouseWheel : function(e)
29479 this.startScale = this.scale;
29481 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29483 if(!this.zoomable()){
29484 this.scale = this.startScale;
29493 zoomable : function()
29495 var minScale = this.thumbEl.getWidth() / this.minWidth;
29497 if(this.minWidth < this.minHeight){
29498 minScale = this.thumbEl.getHeight() / this.minHeight;
29501 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29502 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29506 (this.rotate == 0 || this.rotate == 180) &&
29508 width > this.imageEl.OriginWidth ||
29509 height > this.imageEl.OriginHeight ||
29510 (width < this.minWidth && height < this.minHeight)
29518 (this.rotate == 90 || this.rotate == 270) &&
29520 width > this.imageEl.OriginWidth ||
29521 height > this.imageEl.OriginHeight ||
29522 (width < this.minHeight && height < this.minWidth)
29529 !this.isDocument &&
29530 (this.rotate == 0 || this.rotate == 180) &&
29532 width < this.minWidth ||
29533 width > this.imageEl.OriginWidth ||
29534 height < this.minHeight ||
29535 height > this.imageEl.OriginHeight
29542 !this.isDocument &&
29543 (this.rotate == 90 || this.rotate == 270) &&
29545 width < this.minHeight ||
29546 width > this.imageEl.OriginWidth ||
29547 height < this.minWidth ||
29548 height > this.imageEl.OriginHeight
29558 onRotateLeft : function(e)
29560 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29562 var minScale = this.thumbEl.getWidth() / this.minWidth;
29564 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29565 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29567 this.startScale = this.scale;
29569 while (this.getScaleLevel() < minScale){
29571 this.scale = this.scale + 1;
29573 if(!this.zoomable()){
29578 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29579 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29584 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29591 this.scale = this.startScale;
29593 this.onRotateFail();
29598 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29600 if(this.isDocument){
29601 this.setThumbBoxSize();
29602 this.setThumbBoxPosition();
29603 this.setCanvasPosition();
29608 this.fireEvent('rotate', this, 'left');
29612 onRotateRight : function(e)
29614 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29616 var minScale = this.thumbEl.getWidth() / this.minWidth;
29618 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29619 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29621 this.startScale = this.scale;
29623 while (this.getScaleLevel() < minScale){
29625 this.scale = this.scale + 1;
29627 if(!this.zoomable()){
29632 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29633 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29638 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29645 this.scale = this.startScale;
29647 this.onRotateFail();
29652 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29654 if(this.isDocument){
29655 this.setThumbBoxSize();
29656 this.setThumbBoxPosition();
29657 this.setCanvasPosition();
29662 this.fireEvent('rotate', this, 'right');
29665 onRotateFail : function()
29667 this.errorEl.show(true);
29671 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29676 this.previewEl.dom.innerHTML = '';
29678 var canvasEl = document.createElement("canvas");
29680 var contextEl = canvasEl.getContext("2d");
29682 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29683 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29684 var center = this.imageEl.OriginWidth / 2;
29686 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29687 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29688 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29689 center = this.imageEl.OriginHeight / 2;
29692 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29694 contextEl.translate(center, center);
29695 contextEl.rotate(this.rotate * Math.PI / 180);
29697 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29699 this.canvasEl = document.createElement("canvas");
29701 this.contextEl = this.canvasEl.getContext("2d");
29703 switch (this.rotate) {
29706 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29707 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29709 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29714 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29715 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29717 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29718 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);
29722 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29727 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29728 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29730 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29731 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);
29735 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);
29740 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29741 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29743 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29744 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29748 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);
29755 this.previewEl.appendChild(this.canvasEl);
29757 this.setCanvasPosition();
29762 if(!this.canvasLoaded){
29766 var imageCanvas = document.createElement("canvas");
29768 var imageContext = imageCanvas.getContext("2d");
29770 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29771 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29773 var center = imageCanvas.width / 2;
29775 imageContext.translate(center, center);
29777 imageContext.rotate(this.rotate * Math.PI / 180);
29779 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29781 var canvas = document.createElement("canvas");
29783 var context = canvas.getContext("2d");
29785 canvas.width = this.minWidth;
29786 canvas.height = this.minHeight;
29788 switch (this.rotate) {
29791 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29792 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29794 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29795 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29797 var targetWidth = this.minWidth - 2 * x;
29798 var targetHeight = this.minHeight - 2 * y;
29802 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29803 scale = targetWidth / width;
29806 if(x > 0 && y == 0){
29807 scale = targetHeight / height;
29810 if(x > 0 && y > 0){
29811 scale = targetWidth / width;
29813 if(width < height){
29814 scale = targetHeight / height;
29818 context.scale(scale, scale);
29820 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29821 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29823 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29824 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29826 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29831 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29832 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29834 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29835 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29837 var targetWidth = this.minWidth - 2 * x;
29838 var targetHeight = this.minHeight - 2 * y;
29842 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29843 scale = targetWidth / width;
29846 if(x > 0 && y == 0){
29847 scale = targetHeight / height;
29850 if(x > 0 && y > 0){
29851 scale = targetWidth / width;
29853 if(width < height){
29854 scale = targetHeight / height;
29858 context.scale(scale, scale);
29860 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29861 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29863 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29864 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29866 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29868 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29873 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29874 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29876 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29877 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29879 var targetWidth = this.minWidth - 2 * x;
29880 var targetHeight = this.minHeight - 2 * y;
29884 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29885 scale = targetWidth / width;
29888 if(x > 0 && y == 0){
29889 scale = targetHeight / height;
29892 if(x > 0 && y > 0){
29893 scale = targetWidth / width;
29895 if(width < height){
29896 scale = targetHeight / height;
29900 context.scale(scale, scale);
29902 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29903 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29905 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29906 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29908 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29909 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29911 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29916 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29917 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29919 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29920 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29922 var targetWidth = this.minWidth - 2 * x;
29923 var targetHeight = this.minHeight - 2 * y;
29927 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29928 scale = targetWidth / width;
29931 if(x > 0 && y == 0){
29932 scale = targetHeight / height;
29935 if(x > 0 && y > 0){
29936 scale = targetWidth / width;
29938 if(width < height){
29939 scale = targetHeight / height;
29943 context.scale(scale, scale);
29945 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29946 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29948 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29949 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29951 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29953 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29960 this.cropData = canvas.toDataURL(this.cropType);
29962 if(this.fireEvent('crop', this, this.cropData) !== false){
29963 this.process(this.file, this.cropData);
29970 setThumbBoxSize : function()
29974 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29975 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29976 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29978 this.minWidth = width;
29979 this.minHeight = height;
29981 if(this.rotate == 90 || this.rotate == 270){
29982 this.minWidth = height;
29983 this.minHeight = width;
29988 width = Math.ceil(this.minWidth * height / this.minHeight);
29990 if(this.minWidth > this.minHeight){
29992 height = Math.ceil(this.minHeight * width / this.minWidth);
29995 this.thumbEl.setStyle({
29996 width : width + 'px',
29997 height : height + 'px'
30004 setThumbBoxPosition : function()
30006 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30007 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30009 this.thumbEl.setLeft(x);
30010 this.thumbEl.setTop(y);
30014 baseRotateLevel : function()
30016 this.baseRotate = 1;
30019 typeof(this.exif) != 'undefined' &&
30020 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30021 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30023 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30026 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30030 baseScaleLevel : function()
30034 if(this.isDocument){
30036 if(this.baseRotate == 6 || this.baseRotate == 8){
30038 height = this.thumbEl.getHeight();
30039 this.baseScale = height / this.imageEl.OriginWidth;
30041 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30042 width = this.thumbEl.getWidth();
30043 this.baseScale = width / this.imageEl.OriginHeight;
30049 height = this.thumbEl.getHeight();
30050 this.baseScale = height / this.imageEl.OriginHeight;
30052 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30053 width = this.thumbEl.getWidth();
30054 this.baseScale = width / this.imageEl.OriginWidth;
30060 if(this.baseRotate == 6 || this.baseRotate == 8){
30062 width = this.thumbEl.getHeight();
30063 this.baseScale = width / this.imageEl.OriginHeight;
30065 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30066 height = this.thumbEl.getWidth();
30067 this.baseScale = height / this.imageEl.OriginHeight;
30070 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30071 height = this.thumbEl.getWidth();
30072 this.baseScale = height / this.imageEl.OriginHeight;
30074 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30075 width = this.thumbEl.getHeight();
30076 this.baseScale = width / this.imageEl.OriginWidth;
30083 width = this.thumbEl.getWidth();
30084 this.baseScale = width / this.imageEl.OriginWidth;
30086 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30087 height = this.thumbEl.getHeight();
30088 this.baseScale = height / this.imageEl.OriginHeight;
30091 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30093 height = this.thumbEl.getHeight();
30094 this.baseScale = height / this.imageEl.OriginHeight;
30096 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30097 width = this.thumbEl.getWidth();
30098 this.baseScale = width / this.imageEl.OriginWidth;
30106 getScaleLevel : function()
30108 return this.baseScale * Math.pow(1.1, this.scale);
30111 onTouchStart : function(e)
30113 if(!this.canvasLoaded){
30114 this.beforeSelectFile(e);
30118 var touches = e.browserEvent.touches;
30124 if(touches.length == 1){
30125 this.onMouseDown(e);
30129 if(touches.length != 2){
30135 for(var i = 0, finger; finger = touches[i]; i++){
30136 coords.push(finger.pageX, finger.pageY);
30139 var x = Math.pow(coords[0] - coords[2], 2);
30140 var y = Math.pow(coords[1] - coords[3], 2);
30142 this.startDistance = Math.sqrt(x + y);
30144 this.startScale = this.scale;
30146 this.pinching = true;
30147 this.dragable = false;
30151 onTouchMove : function(e)
30153 if(!this.pinching && !this.dragable){
30157 var touches = e.browserEvent.touches;
30164 this.onMouseMove(e);
30170 for(var i = 0, finger; finger = touches[i]; i++){
30171 coords.push(finger.pageX, finger.pageY);
30174 var x = Math.pow(coords[0] - coords[2], 2);
30175 var y = Math.pow(coords[1] - coords[3], 2);
30177 this.endDistance = Math.sqrt(x + y);
30179 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30181 if(!this.zoomable()){
30182 this.scale = this.startScale;
30190 onTouchEnd : function(e)
30192 this.pinching = false;
30193 this.dragable = false;
30197 process : function(file, crop)
30200 this.maskEl.mask(this.loadingText);
30203 this.xhr = new XMLHttpRequest();
30205 file.xhr = this.xhr;
30207 this.xhr.open(this.method, this.url, true);
30210 "Accept": "application/json",
30211 "Cache-Control": "no-cache",
30212 "X-Requested-With": "XMLHttpRequest"
30215 for (var headerName in headers) {
30216 var headerValue = headers[headerName];
30218 this.xhr.setRequestHeader(headerName, headerValue);
30224 this.xhr.onload = function()
30226 _this.xhrOnLoad(_this.xhr);
30229 this.xhr.onerror = function()
30231 _this.xhrOnError(_this.xhr);
30234 var formData = new FormData();
30236 formData.append('returnHTML', 'NO');
30239 formData.append('crop', crop);
30242 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30243 formData.append(this.paramName, file, file.name);
30246 if(typeof(file.filename) != 'undefined'){
30247 formData.append('filename', file.filename);
30250 if(typeof(file.mimetype) != 'undefined'){
30251 formData.append('mimetype', file.mimetype);
30254 if(this.fireEvent('arrange', this, formData) != false){
30255 this.xhr.send(formData);
30259 xhrOnLoad : function(xhr)
30262 this.maskEl.unmask();
30265 if (xhr.readyState !== 4) {
30266 this.fireEvent('exception', this, xhr);
30270 var response = Roo.decode(xhr.responseText);
30272 if(!response.success){
30273 this.fireEvent('exception', this, xhr);
30277 var response = Roo.decode(xhr.responseText);
30279 this.fireEvent('upload', this, response);
30283 xhrOnError : function()
30286 this.maskEl.unmask();
30289 Roo.log('xhr on error');
30291 var response = Roo.decode(xhr.responseText);
30297 prepare : function(file)
30300 this.maskEl.mask(this.loadingText);
30306 if(typeof(file) === 'string'){
30307 this.loadCanvas(file);
30311 if(!file || !this.urlAPI){
30316 this.cropType = file.type;
30320 if(this.fireEvent('prepare', this, this.file) != false){
30322 var reader = new FileReader();
30324 reader.onload = function (e) {
30325 if (e.target.error) {
30326 Roo.log(e.target.error);
30330 var buffer = e.target.result,
30331 dataView = new DataView(buffer),
30333 maxOffset = dataView.byteLength - 4,
30337 if (dataView.getUint16(0) === 0xffd8) {
30338 while (offset < maxOffset) {
30339 markerBytes = dataView.getUint16(offset);
30341 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30342 markerLength = dataView.getUint16(offset + 2) + 2;
30343 if (offset + markerLength > dataView.byteLength) {
30344 Roo.log('Invalid meta data: Invalid segment size.');
30348 if(markerBytes == 0xffe1){
30349 _this.parseExifData(
30356 offset += markerLength;
30366 var url = _this.urlAPI.createObjectURL(_this.file);
30368 _this.loadCanvas(url);
30373 reader.readAsArrayBuffer(this.file);
30379 parseExifData : function(dataView, offset, length)
30381 var tiffOffset = offset + 10,
30385 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30386 // No Exif data, might be XMP data instead
30390 // Check for the ASCII code for "Exif" (0x45786966):
30391 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30392 // No Exif data, might be XMP data instead
30395 if (tiffOffset + 8 > dataView.byteLength) {
30396 Roo.log('Invalid Exif data: Invalid segment size.');
30399 // Check for the two null bytes:
30400 if (dataView.getUint16(offset + 8) !== 0x0000) {
30401 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30404 // Check the byte alignment:
30405 switch (dataView.getUint16(tiffOffset)) {
30407 littleEndian = true;
30410 littleEndian = false;
30413 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30416 // Check for the TIFF tag marker (0x002A):
30417 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30418 Roo.log('Invalid Exif data: Missing TIFF marker.');
30421 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30422 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30424 this.parseExifTags(
30427 tiffOffset + dirOffset,
30432 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30437 if (dirOffset + 6 > dataView.byteLength) {
30438 Roo.log('Invalid Exif data: Invalid directory offset.');
30441 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30442 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30443 if (dirEndOffset + 4 > dataView.byteLength) {
30444 Roo.log('Invalid Exif data: Invalid directory size.');
30447 for (i = 0; i < tagsNumber; i += 1) {
30451 dirOffset + 2 + 12 * i, // tag offset
30455 // Return the offset to the next directory:
30456 return dataView.getUint32(dirEndOffset, littleEndian);
30459 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30461 var tag = dataView.getUint16(offset, littleEndian);
30463 this.exif[tag] = this.getExifValue(
30467 dataView.getUint16(offset + 2, littleEndian), // tag type
30468 dataView.getUint32(offset + 4, littleEndian), // tag length
30473 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30475 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30484 Roo.log('Invalid Exif data: Invalid tag type.');
30488 tagSize = tagType.size * length;
30489 // Determine if the value is contained in the dataOffset bytes,
30490 // or if the value at the dataOffset is a pointer to the actual data:
30491 dataOffset = tagSize > 4 ?
30492 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30493 if (dataOffset + tagSize > dataView.byteLength) {
30494 Roo.log('Invalid Exif data: Invalid data offset.');
30497 if (length === 1) {
30498 return tagType.getValue(dataView, dataOffset, littleEndian);
30501 for (i = 0; i < length; i += 1) {
30502 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30505 if (tagType.ascii) {
30507 // Concatenate the chars:
30508 for (i = 0; i < values.length; i += 1) {
30510 // Ignore the terminating NULL byte(s):
30511 if (c === '\u0000') {
30523 Roo.apply(Roo.bootstrap.UploadCropbox, {
30525 'Orientation': 0x0112
30529 1: 0, //'top-left',
30531 3: 180, //'bottom-right',
30532 // 4: 'bottom-left',
30534 6: 90, //'right-top',
30535 // 7: 'right-bottom',
30536 8: 270 //'left-bottom'
30540 // byte, 8-bit unsigned int:
30542 getValue: function (dataView, dataOffset) {
30543 return dataView.getUint8(dataOffset);
30547 // ascii, 8-bit byte:
30549 getValue: function (dataView, dataOffset) {
30550 return String.fromCharCode(dataView.getUint8(dataOffset));
30555 // short, 16 bit int:
30557 getValue: function (dataView, dataOffset, littleEndian) {
30558 return dataView.getUint16(dataOffset, littleEndian);
30562 // long, 32 bit int:
30564 getValue: function (dataView, dataOffset, littleEndian) {
30565 return dataView.getUint32(dataOffset, littleEndian);
30569 // rational = two long values, first is numerator, second is denominator:
30571 getValue: function (dataView, dataOffset, littleEndian) {
30572 return dataView.getUint32(dataOffset, littleEndian) /
30573 dataView.getUint32(dataOffset + 4, littleEndian);
30577 // slong, 32 bit signed int:
30579 getValue: function (dataView, dataOffset, littleEndian) {
30580 return dataView.getInt32(dataOffset, littleEndian);
30584 // srational, two slongs, first is numerator, second is denominator:
30586 getValue: function (dataView, dataOffset, littleEndian) {
30587 return dataView.getInt32(dataOffset, littleEndian) /
30588 dataView.getInt32(dataOffset + 4, littleEndian);
30598 cls : 'btn-group roo-upload-cropbox-rotate-left',
30599 action : 'rotate-left',
30603 cls : 'btn btn-default',
30604 html : '<i class="fa fa-undo"></i>'
30610 cls : 'btn-group roo-upload-cropbox-picture',
30611 action : 'picture',
30615 cls : 'btn btn-default',
30616 html : '<i class="fa fa-picture-o"></i>'
30622 cls : 'btn-group roo-upload-cropbox-rotate-right',
30623 action : 'rotate-right',
30627 cls : 'btn btn-default',
30628 html : '<i class="fa fa-repeat"></i>'
30636 cls : 'btn-group roo-upload-cropbox-rotate-left',
30637 action : 'rotate-left',
30641 cls : 'btn btn-default',
30642 html : '<i class="fa fa-undo"></i>'
30648 cls : 'btn-group roo-upload-cropbox-download',
30649 action : 'download',
30653 cls : 'btn btn-default',
30654 html : '<i class="fa fa-download"></i>'
30660 cls : 'btn-group roo-upload-cropbox-crop',
30665 cls : 'btn btn-default',
30666 html : '<i class="fa fa-crop"></i>'
30672 cls : 'btn-group roo-upload-cropbox-trash',
30677 cls : 'btn btn-default',
30678 html : '<i class="fa fa-trash"></i>'
30684 cls : 'btn-group roo-upload-cropbox-rotate-right',
30685 action : 'rotate-right',
30689 cls : 'btn btn-default',
30690 html : '<i class="fa fa-repeat"></i>'
30698 cls : 'btn-group roo-upload-cropbox-rotate-left',
30699 action : 'rotate-left',
30703 cls : 'btn btn-default',
30704 html : '<i class="fa fa-undo"></i>'
30710 cls : 'btn-group roo-upload-cropbox-rotate-right',
30711 action : 'rotate-right',
30715 cls : 'btn btn-default',
30716 html : '<i class="fa fa-repeat"></i>'
30729 * @class Roo.bootstrap.DocumentManager
30730 * @extends Roo.bootstrap.Component
30731 * Bootstrap DocumentManager class
30732 * @cfg {String} paramName default 'imageUpload'
30733 * @cfg {String} toolTipName default 'filename'
30734 * @cfg {String} method default POST
30735 * @cfg {String} url action url
30736 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30737 * @cfg {Boolean} multiple multiple upload default true
30738 * @cfg {Number} thumbSize default 300
30739 * @cfg {String} fieldLabel
30740 * @cfg {Number} labelWidth default 4
30741 * @cfg {String} labelAlign (left|top) default left
30742 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30743 * @cfg {Number} labellg set the width of label (1-12)
30744 * @cfg {Number} labelmd set the width of label (1-12)
30745 * @cfg {Number} labelsm set the width of label (1-12)
30746 * @cfg {Number} labelxs set the width of label (1-12)
30749 * Create a new DocumentManager
30750 * @param {Object} config The config object
30753 Roo.bootstrap.DocumentManager = function(config){
30754 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30757 this.delegates = [];
30762 * Fire when initial the DocumentManager
30763 * @param {Roo.bootstrap.DocumentManager} this
30768 * inspect selected file
30769 * @param {Roo.bootstrap.DocumentManager} this
30770 * @param {File} file
30775 * Fire when xhr load exception
30776 * @param {Roo.bootstrap.DocumentManager} this
30777 * @param {XMLHttpRequest} xhr
30779 "exception" : true,
30781 * @event afterupload
30782 * Fire when xhr load exception
30783 * @param {Roo.bootstrap.DocumentManager} this
30784 * @param {XMLHttpRequest} xhr
30786 "afterupload" : true,
30789 * prepare the form data
30790 * @param {Roo.bootstrap.DocumentManager} this
30791 * @param {Object} formData
30796 * Fire when remove the file
30797 * @param {Roo.bootstrap.DocumentManager} this
30798 * @param {Object} file
30803 * Fire after refresh the file
30804 * @param {Roo.bootstrap.DocumentManager} this
30809 * Fire after click the image
30810 * @param {Roo.bootstrap.DocumentManager} this
30811 * @param {Object} file
30816 * Fire when upload a image and editable set to true
30817 * @param {Roo.bootstrap.DocumentManager} this
30818 * @param {Object} file
30822 * @event beforeselectfile
30823 * Fire before select file
30824 * @param {Roo.bootstrap.DocumentManager} this
30826 "beforeselectfile" : true,
30829 * Fire before process file
30830 * @param {Roo.bootstrap.DocumentManager} this
30831 * @param {Object} file
30835 * @event previewrendered
30836 * Fire when preview rendered
30837 * @param {Roo.bootstrap.DocumentManager} this
30838 * @param {Object} file
30840 "previewrendered" : true,
30843 "previewResize" : true
30848 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
30857 paramName : 'imageUpload',
30858 toolTipName : 'filename',
30861 labelAlign : 'left',
30871 getAutoCreate : function()
30873 var managerWidget = {
30875 cls : 'roo-document-manager',
30879 cls : 'roo-document-manager-selector',
30884 cls : 'roo-document-manager-uploader',
30888 cls : 'roo-document-manager-upload-btn',
30889 html : '<i class="fa fa-plus"></i>'
30900 cls : 'column col-md-12',
30905 if(this.fieldLabel.length){
30910 cls : 'column col-md-12',
30911 html : this.fieldLabel
30915 cls : 'column col-md-12',
30920 if(this.labelAlign == 'left'){
30925 html : this.fieldLabel
30934 if(this.labelWidth > 12){
30935 content[0].style = "width: " + this.labelWidth + 'px';
30938 if(this.labelWidth < 13 && this.labelmd == 0){
30939 this.labelmd = this.labelWidth;
30942 if(this.labellg > 0){
30943 content[0].cls += ' col-lg-' + this.labellg;
30944 content[1].cls += ' col-lg-' + (12 - this.labellg);
30947 if(this.labelmd > 0){
30948 content[0].cls += ' col-md-' + this.labelmd;
30949 content[1].cls += ' col-md-' + (12 - this.labelmd);
30952 if(this.labelsm > 0){
30953 content[0].cls += ' col-sm-' + this.labelsm;
30954 content[1].cls += ' col-sm-' + (12 - this.labelsm);
30957 if(this.labelxs > 0){
30958 content[0].cls += ' col-xs-' + this.labelxs;
30959 content[1].cls += ' col-xs-' + (12 - this.labelxs);
30967 cls : 'row clearfix',
30975 initEvents : function()
30977 this.managerEl = this.el.select('.roo-document-manager', true).first();
30978 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30980 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30981 this.selectorEl.hide();
30984 this.selectorEl.attr('multiple', 'multiple');
30987 this.selectorEl.on('change', this.onFileSelected, this);
30989 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30990 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30992 this.uploader.on('click', this.onUploaderClick, this);
30994 this.renderProgressDialog();
30998 window.addEventListener("resize", function() { _this.refresh(); } );
31000 this.fireEvent('initial', this);
31003 renderProgressDialog : function()
31007 this.progressDialog = new Roo.bootstrap.Modal({
31008 cls : 'roo-document-manager-progress-dialog',
31009 allow_close : false,
31020 btnclick : function() {
31021 _this.uploadCancel();
31027 this.progressDialog.render(Roo.get(document.body));
31029 this.progress = new Roo.bootstrap.Progress({
31030 cls : 'roo-document-manager-progress',
31035 this.progress.render(this.progressDialog.getChildContainer());
31037 this.progressBar = new Roo.bootstrap.ProgressBar({
31038 cls : 'roo-document-manager-progress-bar',
31041 aria_valuemax : 12,
31045 this.progressBar.render(this.progress.getChildContainer());
31048 onUploaderClick : function(e)
31050 e.preventDefault();
31052 if(this.fireEvent('beforeselectfile', this) != false){
31053 this.selectorEl.dom.click();
31058 onFileSelected : function(e)
31060 e.preventDefault();
31062 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31066 Roo.each(this.selectorEl.dom.files, function(file){
31067 if(this.fireEvent('inspect', this, file) != false){
31068 this.files.push(file);
31078 this.selectorEl.dom.value = '';
31080 if(!this.files || !this.files.length){
31084 if(this.boxes > 0 && this.files.length > this.boxes){
31085 this.files = this.files.slice(0, this.boxes);
31088 this.uploader.show();
31090 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31091 this.uploader.hide();
31100 Roo.each(this.files, function(file){
31102 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31103 var f = this.renderPreview(file);
31108 if(file.type.indexOf('image') != -1){
31109 this.delegates.push(
31111 _this.process(file);
31112 }).createDelegate(this)
31120 _this.process(file);
31121 }).createDelegate(this)
31126 this.files = files;
31128 this.delegates = this.delegates.concat(docs);
31130 if(!this.delegates.length){
31135 this.progressBar.aria_valuemax = this.delegates.length;
31142 arrange : function()
31144 if(!this.delegates.length){
31145 this.progressDialog.hide();
31150 var delegate = this.delegates.shift();
31152 this.progressDialog.show();
31154 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31156 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31161 refresh : function()
31163 this.uploader.show();
31165 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31166 this.uploader.hide();
31169 Roo.isTouch ? this.closable(false) : this.closable(true);
31171 this.fireEvent('refresh', this);
31174 onRemove : function(e, el, o)
31176 e.preventDefault();
31178 this.fireEvent('remove', this, o);
31182 remove : function(o)
31186 Roo.each(this.files, function(file){
31187 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31196 this.files = files;
31203 Roo.each(this.files, function(file){
31208 file.target.remove();
31217 onClick : function(e, el, o)
31219 e.preventDefault();
31221 this.fireEvent('click', this, o);
31225 closable : function(closable)
31227 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31229 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31241 xhrOnLoad : function(xhr)
31243 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31247 if (xhr.readyState !== 4) {
31249 this.fireEvent('exception', this, xhr);
31253 var response = Roo.decode(xhr.responseText);
31255 if(!response.success){
31257 this.fireEvent('exception', this, xhr);
31261 var file = this.renderPreview(response.data);
31263 this.files.push(file);
31267 this.fireEvent('afterupload', this, xhr);
31271 xhrOnError : function(xhr)
31273 Roo.log('xhr on error');
31275 var response = Roo.decode(xhr.responseText);
31282 process : function(file)
31284 if(this.fireEvent('process', this, file) !== false){
31285 if(this.editable && file.type.indexOf('image') != -1){
31286 this.fireEvent('edit', this, file);
31290 this.uploadStart(file, false);
31297 uploadStart : function(file, crop)
31299 this.xhr = new XMLHttpRequest();
31301 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31306 file.xhr = this.xhr;
31308 this.managerEl.createChild({
31310 cls : 'roo-document-manager-loading',
31314 tooltip : file.name,
31315 cls : 'roo-document-manager-thumb',
31316 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31322 this.xhr.open(this.method, this.url, true);
31325 "Accept": "application/json",
31326 "Cache-Control": "no-cache",
31327 "X-Requested-With": "XMLHttpRequest"
31330 for (var headerName in headers) {
31331 var headerValue = headers[headerName];
31333 this.xhr.setRequestHeader(headerName, headerValue);
31339 this.xhr.onload = function()
31341 _this.xhrOnLoad(_this.xhr);
31344 this.xhr.onerror = function()
31346 _this.xhrOnError(_this.xhr);
31349 var formData = new FormData();
31351 formData.append('returnHTML', 'NO');
31354 formData.append('crop', crop);
31357 formData.append(this.paramName, file, file.name);
31364 if(this.fireEvent('prepare', this, formData, options) != false){
31366 if(options.manually){
31370 this.xhr.send(formData);
31374 this.uploadCancel();
31377 uploadCancel : function()
31383 this.delegates = [];
31385 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31392 renderPreview : function(file)
31394 if(typeof(file.target) != 'undefined' && file.target){
31398 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31400 var previewEl = this.managerEl.createChild({
31402 cls : 'roo-document-manager-preview',
31406 tooltip : file[this.toolTipName],
31407 cls : 'roo-document-manager-thumb',
31408 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31413 html : '<i class="fa fa-times-circle"></i>'
31418 var close = previewEl.select('button.close', true).first();
31420 close.on('click', this.onRemove, this, file);
31422 file.target = previewEl;
31424 var image = previewEl.select('img', true).first();
31428 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31430 image.on('click', this.onClick, this, file);
31432 this.fireEvent('previewrendered', this, file);
31438 onPreviewLoad : function(file, image)
31440 if(typeof(file.target) == 'undefined' || !file.target){
31444 var width = image.dom.naturalWidth || image.dom.width;
31445 var height = image.dom.naturalHeight || image.dom.height;
31447 if(!this.previewResize) {
31451 if(width > height){
31452 file.target.addClass('wide');
31456 file.target.addClass('tall');
31461 uploadFromSource : function(file, crop)
31463 this.xhr = new XMLHttpRequest();
31465 this.managerEl.createChild({
31467 cls : 'roo-document-manager-loading',
31471 tooltip : file.name,
31472 cls : 'roo-document-manager-thumb',
31473 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31479 this.xhr.open(this.method, this.url, true);
31482 "Accept": "application/json",
31483 "Cache-Control": "no-cache",
31484 "X-Requested-With": "XMLHttpRequest"
31487 for (var headerName in headers) {
31488 var headerValue = headers[headerName];
31490 this.xhr.setRequestHeader(headerName, headerValue);
31496 this.xhr.onload = function()
31498 _this.xhrOnLoad(_this.xhr);
31501 this.xhr.onerror = function()
31503 _this.xhrOnError(_this.xhr);
31506 var formData = new FormData();
31508 formData.append('returnHTML', 'NO');
31510 formData.append('crop', crop);
31512 if(typeof(file.filename) != 'undefined'){
31513 formData.append('filename', file.filename);
31516 if(typeof(file.mimetype) != 'undefined'){
31517 formData.append('mimetype', file.mimetype);
31522 if(this.fireEvent('prepare', this, formData) != false){
31523 this.xhr.send(formData);
31533 * @class Roo.bootstrap.DocumentViewer
31534 * @extends Roo.bootstrap.Component
31535 * Bootstrap DocumentViewer class
31536 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31537 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31540 * Create a new DocumentViewer
31541 * @param {Object} config The config object
31544 Roo.bootstrap.DocumentViewer = function(config){
31545 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31550 * Fire after initEvent
31551 * @param {Roo.bootstrap.DocumentViewer} this
31557 * @param {Roo.bootstrap.DocumentViewer} this
31562 * Fire after download button
31563 * @param {Roo.bootstrap.DocumentViewer} this
31568 * Fire after trash button
31569 * @param {Roo.bootstrap.DocumentViewer} this
31576 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31578 showDownload : true,
31582 getAutoCreate : function()
31586 cls : 'roo-document-viewer',
31590 cls : 'roo-document-viewer-body',
31594 cls : 'roo-document-viewer-thumb',
31598 cls : 'roo-document-viewer-image'
31606 cls : 'roo-document-viewer-footer',
31609 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31613 cls : 'btn-group roo-document-viewer-download',
31617 cls : 'btn btn-default',
31618 html : '<i class="fa fa-download"></i>'
31624 cls : 'btn-group roo-document-viewer-trash',
31628 cls : 'btn btn-default',
31629 html : '<i class="fa fa-trash"></i>'
31642 initEvents : function()
31644 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31645 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31647 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31648 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31650 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31651 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31653 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31654 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31656 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31657 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31659 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31660 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31662 this.bodyEl.on('click', this.onClick, this);
31663 this.downloadBtn.on('click', this.onDownload, this);
31664 this.trashBtn.on('click', this.onTrash, this);
31666 this.downloadBtn.hide();
31667 this.trashBtn.hide();
31669 if(this.showDownload){
31670 this.downloadBtn.show();
31673 if(this.showTrash){
31674 this.trashBtn.show();
31677 if(!this.showDownload && !this.showTrash) {
31678 this.footerEl.hide();
31683 initial : function()
31685 this.fireEvent('initial', this);
31689 onClick : function(e)
31691 e.preventDefault();
31693 this.fireEvent('click', this);
31696 onDownload : function(e)
31698 e.preventDefault();
31700 this.fireEvent('download', this);
31703 onTrash : function(e)
31705 e.preventDefault();
31707 this.fireEvent('trash', this);
31719 * @class Roo.bootstrap.NavProgressBar
31720 * @extends Roo.bootstrap.Component
31721 * Bootstrap NavProgressBar class
31724 * Create a new nav progress bar
31725 * @param {Object} config The config object
31728 Roo.bootstrap.NavProgressBar = function(config){
31729 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31731 this.bullets = this.bullets || [];
31733 // Roo.bootstrap.NavProgressBar.register(this);
31737 * Fires when the active item changes
31738 * @param {Roo.bootstrap.NavProgressBar} this
31739 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31740 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31747 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31752 getAutoCreate : function()
31754 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31758 cls : 'roo-navigation-bar-group',
31762 cls : 'roo-navigation-top-bar'
31766 cls : 'roo-navigation-bullets-bar',
31770 cls : 'roo-navigation-bar'
31777 cls : 'roo-navigation-bottom-bar'
31787 initEvents: function()
31792 onRender : function(ct, position)
31794 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31796 if(this.bullets.length){
31797 Roo.each(this.bullets, function(b){
31806 addItem : function(cfg)
31808 var item = new Roo.bootstrap.NavProgressItem(cfg);
31810 item.parentId = this.id;
31811 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31814 var top = new Roo.bootstrap.Element({
31816 cls : 'roo-navigation-bar-text'
31819 var bottom = new Roo.bootstrap.Element({
31821 cls : 'roo-navigation-bar-text'
31824 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31825 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31827 var topText = new Roo.bootstrap.Element({
31829 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31832 var bottomText = new Roo.bootstrap.Element({
31834 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31837 topText.onRender(top.el, null);
31838 bottomText.onRender(bottom.el, null);
31841 item.bottomEl = bottom;
31844 this.barItems.push(item);
31849 getActive : function()
31851 var active = false;
31853 Roo.each(this.barItems, function(v){
31855 if (!v.isActive()) {
31867 setActiveItem : function(item)
31871 Roo.each(this.barItems, function(v){
31872 if (v.rid == item.rid) {
31876 if (v.isActive()) {
31877 v.setActive(false);
31882 item.setActive(true);
31884 this.fireEvent('changed', this, item, prev);
31887 getBarItem: function(rid)
31891 Roo.each(this.barItems, function(e) {
31892 if (e.rid != rid) {
31903 indexOfItem : function(item)
31907 Roo.each(this.barItems, function(v, i){
31909 if (v.rid != item.rid) {
31920 setActiveNext : function()
31922 var i = this.indexOfItem(this.getActive());
31924 if (i > this.barItems.length) {
31928 this.setActiveItem(this.barItems[i+1]);
31931 setActivePrev : function()
31933 var i = this.indexOfItem(this.getActive());
31939 this.setActiveItem(this.barItems[i-1]);
31942 format : function()
31944 if(!this.barItems.length){
31948 var width = 100 / this.barItems.length;
31950 Roo.each(this.barItems, function(i){
31951 i.el.setStyle('width', width + '%');
31952 i.topEl.el.setStyle('width', width + '%');
31953 i.bottomEl.el.setStyle('width', width + '%');
31962 * Nav Progress Item
31967 * @class Roo.bootstrap.NavProgressItem
31968 * @extends Roo.bootstrap.Component
31969 * Bootstrap NavProgressItem class
31970 * @cfg {String} rid the reference id
31971 * @cfg {Boolean} active (true|false) Is item active default false
31972 * @cfg {Boolean} disabled (true|false) Is item active default false
31973 * @cfg {String} html
31974 * @cfg {String} position (top|bottom) text position default bottom
31975 * @cfg {String} icon show icon instead of number
31978 * Create a new NavProgressItem
31979 * @param {Object} config The config object
31981 Roo.bootstrap.NavProgressItem = function(config){
31982 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31987 * The raw click event for the entire grid.
31988 * @param {Roo.bootstrap.NavProgressItem} this
31989 * @param {Roo.EventObject} e
31996 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32002 position : 'bottom',
32005 getAutoCreate : function()
32007 var iconCls = 'roo-navigation-bar-item-icon';
32009 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32013 cls: 'roo-navigation-bar-item',
32023 cfg.cls += ' active';
32026 cfg.cls += ' disabled';
32032 disable : function()
32034 this.setDisabled(true);
32037 enable : function()
32039 this.setDisabled(false);
32042 initEvents: function()
32044 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32046 this.iconEl.on('click', this.onClick, this);
32049 onClick : function(e)
32051 e.preventDefault();
32057 if(this.fireEvent('click', this, e) === false){
32061 this.parent().setActiveItem(this);
32064 isActive: function ()
32066 return this.active;
32069 setActive : function(state)
32071 if(this.active == state){
32075 this.active = state;
32078 this.el.addClass('active');
32082 this.el.removeClass('active');
32087 setDisabled : function(state)
32089 if(this.disabled == state){
32093 this.disabled = state;
32096 this.el.addClass('disabled');
32100 this.el.removeClass('disabled');
32103 tooltipEl : function()
32105 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32118 * @class Roo.bootstrap.FieldLabel
32119 * @extends Roo.bootstrap.Component
32120 * Bootstrap FieldLabel class
32121 * @cfg {String} html contents of the element
32122 * @cfg {String} tag tag of the element default label
32123 * @cfg {String} cls class of the element
32124 * @cfg {String} target label target
32125 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32126 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32127 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32128 * @cfg {String} iconTooltip default "This field is required"
32129 * @cfg {String} indicatorpos (left|right) default left
32132 * Create a new FieldLabel
32133 * @param {Object} config The config object
32136 Roo.bootstrap.FieldLabel = function(config){
32137 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32142 * Fires after the field has been marked as invalid.
32143 * @param {Roo.form.FieldLabel} this
32144 * @param {String} msg The validation message
32149 * Fires after the field has been validated with no errors.
32150 * @param {Roo.form.FieldLabel} this
32156 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32163 invalidClass : 'has-warning',
32164 validClass : 'has-success',
32165 iconTooltip : 'This field is required',
32166 indicatorpos : 'left',
32168 getAutoCreate : function(){
32171 if (!this.allowBlank) {
32177 cls : 'roo-bootstrap-field-label ' + this.cls,
32182 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32183 tooltip : this.iconTooltip
32192 if(this.indicatorpos == 'right'){
32195 cls : 'roo-bootstrap-field-label ' + this.cls,
32204 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32205 tooltip : this.iconTooltip
32214 initEvents: function()
32216 Roo.bootstrap.Element.superclass.initEvents.call(this);
32218 this.indicator = this.indicatorEl();
32220 if(this.indicator){
32221 this.indicator.removeClass('visible');
32222 this.indicator.addClass('invisible');
32225 Roo.bootstrap.FieldLabel.register(this);
32228 indicatorEl : function()
32230 var indicator = this.el.select('i.roo-required-indicator',true).first();
32241 * Mark this field as valid
32243 markValid : function()
32245 if(this.indicator){
32246 this.indicator.removeClass('visible');
32247 this.indicator.addClass('invisible');
32249 if (Roo.bootstrap.version == 3) {
32250 this.el.removeClass(this.invalidClass);
32251 this.el.addClass(this.validClass);
32253 this.el.removeClass('is-invalid');
32254 this.el.addClass('is-valid');
32258 this.fireEvent('valid', this);
32262 * Mark this field as invalid
32263 * @param {String} msg The validation message
32265 markInvalid : function(msg)
32267 if(this.indicator){
32268 this.indicator.removeClass('invisible');
32269 this.indicator.addClass('visible');
32271 if (Roo.bootstrap.version == 3) {
32272 this.el.removeClass(this.validClass);
32273 this.el.addClass(this.invalidClass);
32275 this.el.removeClass('is-valid');
32276 this.el.addClass('is-invalid');
32280 this.fireEvent('invalid', this, msg);
32286 Roo.apply(Roo.bootstrap.FieldLabel, {
32291 * register a FieldLabel Group
32292 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32294 register : function(label)
32296 if(this.groups.hasOwnProperty(label.target)){
32300 this.groups[label.target] = label;
32304 * fetch a FieldLabel Group based on the target
32305 * @param {string} target
32306 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32308 get: function(target) {
32309 if (typeof(this.groups[target]) == 'undefined') {
32313 return this.groups[target] ;
32322 * page DateSplitField.
32328 * @class Roo.bootstrap.DateSplitField
32329 * @extends Roo.bootstrap.Component
32330 * Bootstrap DateSplitField class
32331 * @cfg {string} fieldLabel - the label associated
32332 * @cfg {Number} labelWidth set the width of label (0-12)
32333 * @cfg {String} labelAlign (top|left)
32334 * @cfg {Boolean} dayAllowBlank (true|false) default false
32335 * @cfg {Boolean} monthAllowBlank (true|false) default false
32336 * @cfg {Boolean} yearAllowBlank (true|false) default false
32337 * @cfg {string} dayPlaceholder
32338 * @cfg {string} monthPlaceholder
32339 * @cfg {string} yearPlaceholder
32340 * @cfg {string} dayFormat default 'd'
32341 * @cfg {string} monthFormat default 'm'
32342 * @cfg {string} yearFormat default 'Y'
32343 * @cfg {Number} labellg set the width of label (1-12)
32344 * @cfg {Number} labelmd set the width of label (1-12)
32345 * @cfg {Number} labelsm set the width of label (1-12)
32346 * @cfg {Number} labelxs set the width of label (1-12)
32350 * Create a new DateSplitField
32351 * @param {Object} config The config object
32354 Roo.bootstrap.DateSplitField = function(config){
32355 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32361 * getting the data of years
32362 * @param {Roo.bootstrap.DateSplitField} this
32363 * @param {Object} years
32368 * getting the data of days
32369 * @param {Roo.bootstrap.DateSplitField} this
32370 * @param {Object} days
32375 * Fires after the field has been marked as invalid.
32376 * @param {Roo.form.Field} this
32377 * @param {String} msg The validation message
32382 * Fires after the field has been validated with no errors.
32383 * @param {Roo.form.Field} this
32389 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32392 labelAlign : 'top',
32394 dayAllowBlank : false,
32395 monthAllowBlank : false,
32396 yearAllowBlank : false,
32397 dayPlaceholder : '',
32398 monthPlaceholder : '',
32399 yearPlaceholder : '',
32403 isFormField : true,
32409 getAutoCreate : function()
32413 cls : 'row roo-date-split-field-group',
32418 cls : 'form-hidden-field roo-date-split-field-group-value',
32424 var labelCls = 'col-md-12';
32425 var contentCls = 'col-md-4';
32427 if(this.fieldLabel){
32431 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32435 html : this.fieldLabel
32440 if(this.labelAlign == 'left'){
32442 if(this.labelWidth > 12){
32443 label.style = "width: " + this.labelWidth + 'px';
32446 if(this.labelWidth < 13 && this.labelmd == 0){
32447 this.labelmd = this.labelWidth;
32450 if(this.labellg > 0){
32451 labelCls = ' col-lg-' + this.labellg;
32452 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32455 if(this.labelmd > 0){
32456 labelCls = ' col-md-' + this.labelmd;
32457 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32460 if(this.labelsm > 0){
32461 labelCls = ' col-sm-' + this.labelsm;
32462 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32465 if(this.labelxs > 0){
32466 labelCls = ' col-xs-' + this.labelxs;
32467 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32471 label.cls += ' ' + labelCls;
32473 cfg.cn.push(label);
32476 Roo.each(['day', 'month', 'year'], function(t){
32479 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32486 inputEl: function ()
32488 return this.el.select('.roo-date-split-field-group-value', true).first();
32491 onRender : function(ct, position)
32495 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32497 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32499 this.dayField = new Roo.bootstrap.ComboBox({
32500 allowBlank : this.dayAllowBlank,
32501 alwaysQuery : true,
32502 displayField : 'value',
32505 forceSelection : true,
32507 placeholder : this.dayPlaceholder,
32508 selectOnFocus : true,
32509 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32510 triggerAction : 'all',
32512 valueField : 'value',
32513 store : new Roo.data.SimpleStore({
32514 data : (function() {
32516 _this.fireEvent('days', _this, days);
32519 fields : [ 'value' ]
32522 select : function (_self, record, index)
32524 _this.setValue(_this.getValue());
32529 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32531 this.monthField = new Roo.bootstrap.MonthField({
32532 after : '<i class=\"fa fa-calendar\"></i>',
32533 allowBlank : this.monthAllowBlank,
32534 placeholder : this.monthPlaceholder,
32537 render : function (_self)
32539 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32540 e.preventDefault();
32544 select : function (_self, oldvalue, newvalue)
32546 _this.setValue(_this.getValue());
32551 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32553 this.yearField = new Roo.bootstrap.ComboBox({
32554 allowBlank : this.yearAllowBlank,
32555 alwaysQuery : true,
32556 displayField : 'value',
32559 forceSelection : true,
32561 placeholder : this.yearPlaceholder,
32562 selectOnFocus : true,
32563 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32564 triggerAction : 'all',
32566 valueField : 'value',
32567 store : new Roo.data.SimpleStore({
32568 data : (function() {
32570 _this.fireEvent('years', _this, years);
32573 fields : [ 'value' ]
32576 select : function (_self, record, index)
32578 _this.setValue(_this.getValue());
32583 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32586 setValue : function(v, format)
32588 this.inputEl.dom.value = v;
32590 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32592 var d = Date.parseDate(v, f);
32599 this.setDay(d.format(this.dayFormat));
32600 this.setMonth(d.format(this.monthFormat));
32601 this.setYear(d.format(this.yearFormat));
32608 setDay : function(v)
32610 this.dayField.setValue(v);
32611 this.inputEl.dom.value = this.getValue();
32616 setMonth : function(v)
32618 this.monthField.setValue(v, true);
32619 this.inputEl.dom.value = this.getValue();
32624 setYear : function(v)
32626 this.yearField.setValue(v);
32627 this.inputEl.dom.value = this.getValue();
32632 getDay : function()
32634 return this.dayField.getValue();
32637 getMonth : function()
32639 return this.monthField.getValue();
32642 getYear : function()
32644 return this.yearField.getValue();
32647 getValue : function()
32649 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32651 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32661 this.inputEl.dom.value = '';
32666 validate : function()
32668 var d = this.dayField.validate();
32669 var m = this.monthField.validate();
32670 var y = this.yearField.validate();
32675 (!this.dayAllowBlank && !d) ||
32676 (!this.monthAllowBlank && !m) ||
32677 (!this.yearAllowBlank && !y)
32682 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32691 this.markInvalid();
32696 markValid : function()
32699 var label = this.el.select('label', true).first();
32700 var icon = this.el.select('i.fa-star', true).first();
32706 this.fireEvent('valid', this);
32710 * Mark this field as invalid
32711 * @param {String} msg The validation message
32713 markInvalid : function(msg)
32716 var label = this.el.select('label', true).first();
32717 var icon = this.el.select('i.fa-star', true).first();
32719 if(label && !icon){
32720 this.el.select('.roo-date-split-field-label', true).createChild({
32722 cls : 'text-danger fa fa-lg fa-star',
32723 tooltip : 'This field is required',
32724 style : 'margin-right:5px;'
32728 this.fireEvent('invalid', this, msg);
32731 clearInvalid : function()
32733 var label = this.el.select('label', true).first();
32734 var icon = this.el.select('i.fa-star', true).first();
32740 this.fireEvent('valid', this);
32743 getName: function()
32753 * http://masonry.desandro.com
32755 * The idea is to render all the bricks based on vertical width...
32757 * The original code extends 'outlayer' - we might need to use that....
32763 * @class Roo.bootstrap.LayoutMasonry
32764 * @extends Roo.bootstrap.Component
32765 * Bootstrap Layout Masonry class
32768 * Create a new Element
32769 * @param {Object} config The config object
32772 Roo.bootstrap.LayoutMasonry = function(config){
32774 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32778 Roo.bootstrap.LayoutMasonry.register(this);
32784 * Fire after layout the items
32785 * @param {Roo.bootstrap.LayoutMasonry} this
32786 * @param {Roo.EventObject} e
32793 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
32796 * @cfg {Boolean} isLayoutInstant = no animation?
32798 isLayoutInstant : false, // needed?
32801 * @cfg {Number} boxWidth width of the columns
32806 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
32811 * @cfg {Number} padWidth padding below box..
32816 * @cfg {Number} gutter gutter width..
32821 * @cfg {Number} maxCols maximum number of columns
32827 * @cfg {Boolean} isAutoInitial defalut true
32829 isAutoInitial : true,
32834 * @cfg {Boolean} isHorizontal defalut false
32836 isHorizontal : false,
32838 currentSize : null,
32844 bricks: null, //CompositeElement
32848 _isLayoutInited : false,
32850 // isAlternative : false, // only use for vertical layout...
32853 * @cfg {Number} alternativePadWidth padding below box..
32855 alternativePadWidth : 50,
32857 selectedBrick : [],
32859 getAutoCreate : function(){
32861 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32865 cls: 'blog-masonary-wrapper ' + this.cls,
32867 cls : 'mas-boxes masonary'
32874 getChildContainer: function( )
32876 if (this.boxesEl) {
32877 return this.boxesEl;
32880 this.boxesEl = this.el.select('.mas-boxes').first();
32882 return this.boxesEl;
32886 initEvents : function()
32890 if(this.isAutoInitial){
32891 Roo.log('hook children rendered');
32892 this.on('childrenrendered', function() {
32893 Roo.log('children rendered');
32899 initial : function()
32901 this.selectedBrick = [];
32903 this.currentSize = this.el.getBox(true);
32905 Roo.EventManager.onWindowResize(this.resize, this);
32907 if(!this.isAutoInitial){
32915 //this.layout.defer(500,this);
32919 resize : function()
32921 var cs = this.el.getBox(true);
32924 this.currentSize.width == cs.width &&
32925 this.currentSize.x == cs.x &&
32926 this.currentSize.height == cs.height &&
32927 this.currentSize.y == cs.y
32929 Roo.log("no change in with or X or Y");
32933 this.currentSize = cs;
32939 layout : function()
32941 this._resetLayout();
32943 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32945 this.layoutItems( isInstant );
32947 this._isLayoutInited = true;
32949 this.fireEvent('layout', this);
32953 _resetLayout : function()
32955 if(this.isHorizontal){
32956 this.horizontalMeasureColumns();
32960 this.verticalMeasureColumns();
32964 verticalMeasureColumns : function()
32966 this.getContainerWidth();
32968 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32969 // this.colWidth = Math.floor(this.containerWidth * 0.8);
32973 var boxWidth = this.boxWidth + this.padWidth;
32975 if(this.containerWidth < this.boxWidth){
32976 boxWidth = this.containerWidth
32979 var containerWidth = this.containerWidth;
32981 var cols = Math.floor(containerWidth / boxWidth);
32983 this.cols = Math.max( cols, 1 );
32985 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32987 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32989 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32991 this.colWidth = boxWidth + avail - this.padWidth;
32993 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32994 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
32997 horizontalMeasureColumns : function()
32999 this.getContainerWidth();
33001 var boxWidth = this.boxWidth;
33003 if(this.containerWidth < boxWidth){
33004 boxWidth = this.containerWidth;
33007 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33009 this.el.setHeight(boxWidth);
33013 getContainerWidth : function()
33015 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33018 layoutItems : function( isInstant )
33020 Roo.log(this.bricks);
33022 var items = Roo.apply([], this.bricks);
33024 if(this.isHorizontal){
33025 this._horizontalLayoutItems( items , isInstant );
33029 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33030 // this._verticalAlternativeLayoutItems( items , isInstant );
33034 this._verticalLayoutItems( items , isInstant );
33038 _verticalLayoutItems : function ( items , isInstant)
33040 if ( !items || !items.length ) {
33045 ['xs', 'xs', 'xs', 'tall'],
33046 ['xs', 'xs', 'tall'],
33047 ['xs', 'xs', 'sm'],
33048 ['xs', 'xs', 'xs'],
33054 ['sm', 'xs', 'xs'],
33058 ['tall', 'xs', 'xs', 'xs'],
33059 ['tall', 'xs', 'xs'],
33071 Roo.each(items, function(item, k){
33073 switch (item.size) {
33074 // these layouts take up a full box,
33085 boxes.push([item]);
33108 var filterPattern = function(box, length)
33116 var pattern = box.slice(0, length);
33120 Roo.each(pattern, function(i){
33121 format.push(i.size);
33124 Roo.each(standard, function(s){
33126 if(String(s) != String(format)){
33135 if(!match && length == 1){
33140 filterPattern(box, length - 1);
33144 queue.push(pattern);
33146 box = box.slice(length, box.length);
33148 filterPattern(box, 4);
33154 Roo.each(boxes, function(box, k){
33160 if(box.length == 1){
33165 filterPattern(box, 4);
33169 this._processVerticalLayoutQueue( queue, isInstant );
33173 // _verticalAlternativeLayoutItems : function( items , isInstant )
33175 // if ( !items || !items.length ) {
33179 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33183 _horizontalLayoutItems : function ( items , isInstant)
33185 if ( !items || !items.length || items.length < 3) {
33191 var eItems = items.slice(0, 3);
33193 items = items.slice(3, items.length);
33196 ['xs', 'xs', 'xs', 'wide'],
33197 ['xs', 'xs', 'wide'],
33198 ['xs', 'xs', 'sm'],
33199 ['xs', 'xs', 'xs'],
33205 ['sm', 'xs', 'xs'],
33209 ['wide', 'xs', 'xs', 'xs'],
33210 ['wide', 'xs', 'xs'],
33223 Roo.each(items, function(item, k){
33225 switch (item.size) {
33236 boxes.push([item]);
33260 var filterPattern = function(box, length)
33268 var pattern = box.slice(0, length);
33272 Roo.each(pattern, function(i){
33273 format.push(i.size);
33276 Roo.each(standard, function(s){
33278 if(String(s) != String(format)){
33287 if(!match && length == 1){
33292 filterPattern(box, length - 1);
33296 queue.push(pattern);
33298 box = box.slice(length, box.length);
33300 filterPattern(box, 4);
33306 Roo.each(boxes, function(box, k){
33312 if(box.length == 1){
33317 filterPattern(box, 4);
33324 var pos = this.el.getBox(true);
33328 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33330 var hit_end = false;
33332 Roo.each(queue, function(box){
33336 Roo.each(box, function(b){
33338 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33348 Roo.each(box, function(b){
33350 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33353 mx = Math.max(mx, b.x);
33357 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33361 Roo.each(box, function(b){
33363 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33377 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33380 /** Sets position of item in DOM
33381 * @param {Element} item
33382 * @param {Number} x - horizontal position
33383 * @param {Number} y - vertical position
33384 * @param {Boolean} isInstant - disables transitions
33386 _processVerticalLayoutQueue : function( queue, isInstant )
33388 var pos = this.el.getBox(true);
33393 for (var i = 0; i < this.cols; i++){
33397 Roo.each(queue, function(box, k){
33399 var col = k % this.cols;
33401 Roo.each(box, function(b,kk){
33403 b.el.position('absolute');
33405 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33406 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33408 if(b.size == 'md-left' || b.size == 'md-right'){
33409 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33410 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33413 b.el.setWidth(width);
33414 b.el.setHeight(height);
33416 b.el.select('iframe',true).setSize(width,height);
33420 for (var i = 0; i < this.cols; i++){
33422 if(maxY[i] < maxY[col]){
33427 col = Math.min(col, i);
33431 x = pos.x + col * (this.colWidth + this.padWidth);
33435 var positions = [];
33437 switch (box.length){
33439 positions = this.getVerticalOneBoxColPositions(x, y, box);
33442 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33445 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33448 positions = this.getVerticalFourBoxColPositions(x, y, box);
33454 Roo.each(box, function(b,kk){
33456 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33458 var sz = b.el.getSize();
33460 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33468 for (var i = 0; i < this.cols; i++){
33469 mY = Math.max(mY, maxY[i]);
33472 this.el.setHeight(mY - pos.y);
33476 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33478 // var pos = this.el.getBox(true);
33481 // var maxX = pos.right;
33483 // var maxHeight = 0;
33485 // Roo.each(items, function(item, k){
33489 // item.el.position('absolute');
33491 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33493 // item.el.setWidth(width);
33495 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33497 // item.el.setHeight(height);
33500 // item.el.setXY([x, y], isInstant ? false : true);
33502 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33505 // y = y + height + this.alternativePadWidth;
33507 // maxHeight = maxHeight + height + this.alternativePadWidth;
33511 // this.el.setHeight(maxHeight);
33515 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33517 var pos = this.el.getBox(true);
33522 var maxX = pos.right;
33524 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33526 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33528 Roo.each(queue, function(box, k){
33530 Roo.each(box, function(b, kk){
33532 b.el.position('absolute');
33534 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33535 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33537 if(b.size == 'md-left' || b.size == 'md-right'){
33538 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33539 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33542 b.el.setWidth(width);
33543 b.el.setHeight(height);
33551 var positions = [];
33553 switch (box.length){
33555 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33558 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33561 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33564 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33570 Roo.each(box, function(b,kk){
33572 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33574 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33582 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33584 Roo.each(eItems, function(b,k){
33586 b.size = (k == 0) ? 'sm' : 'xs';
33587 b.x = (k == 0) ? 2 : 1;
33588 b.y = (k == 0) ? 2 : 1;
33590 b.el.position('absolute');
33592 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33594 b.el.setWidth(width);
33596 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33598 b.el.setHeight(height);
33602 var positions = [];
33605 x : maxX - this.unitWidth * 2 - this.gutter,
33610 x : maxX - this.unitWidth,
33611 y : minY + (this.unitWidth + this.gutter) * 2
33615 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33619 Roo.each(eItems, function(b,k){
33621 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33627 getVerticalOneBoxColPositions : function(x, y, box)
33631 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33633 if(box[0].size == 'md-left'){
33637 if(box[0].size == 'md-right'){
33642 x : x + (this.unitWidth + this.gutter) * rand,
33649 getVerticalTwoBoxColPositions : function(x, y, box)
33653 if(box[0].size == 'xs'){
33657 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33661 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33675 x : x + (this.unitWidth + this.gutter) * 2,
33676 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33683 getVerticalThreeBoxColPositions : function(x, y, box)
33687 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33695 x : x + (this.unitWidth + this.gutter) * 1,
33700 x : x + (this.unitWidth + this.gutter) * 2,
33708 if(box[0].size == 'xs' && box[1].size == 'xs'){
33717 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33721 x : x + (this.unitWidth + this.gutter) * 1,
33735 x : x + (this.unitWidth + this.gutter) * 2,
33740 x : x + (this.unitWidth + this.gutter) * 2,
33741 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33748 getVerticalFourBoxColPositions : function(x, y, box)
33752 if(box[0].size == 'xs'){
33761 y : y + (this.unitHeight + this.gutter) * 1
33766 y : y + (this.unitHeight + this.gutter) * 2
33770 x : x + (this.unitWidth + this.gutter) * 1,
33784 x : x + (this.unitWidth + this.gutter) * 2,
33789 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33790 y : y + (this.unitHeight + this.gutter) * 1
33794 x : x + (this.unitWidth + this.gutter) * 2,
33795 y : y + (this.unitWidth + this.gutter) * 2
33802 getHorizontalOneBoxColPositions : function(maxX, minY, box)
33806 if(box[0].size == 'md-left'){
33808 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33815 if(box[0].size == 'md-right'){
33817 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33818 y : minY + (this.unitWidth + this.gutter) * 1
33824 var rand = Math.floor(Math.random() * (4 - box[0].y));
33827 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33828 y : minY + (this.unitWidth + this.gutter) * rand
33835 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33839 if(box[0].size == 'xs'){
33842 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33847 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33848 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33856 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33861 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33862 y : minY + (this.unitWidth + this.gutter) * 2
33869 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33873 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33876 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33881 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33882 y : minY + (this.unitWidth + this.gutter) * 1
33886 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33887 y : minY + (this.unitWidth + this.gutter) * 2
33894 if(box[0].size == 'xs' && box[1].size == 'xs'){
33897 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33902 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33907 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33908 y : minY + (this.unitWidth + this.gutter) * 1
33916 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33921 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33922 y : minY + (this.unitWidth + this.gutter) * 2
33926 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33927 y : minY + (this.unitWidth + this.gutter) * 2
33934 getHorizontalFourBoxColPositions : function(maxX, minY, box)
33938 if(box[0].size == 'xs'){
33941 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33946 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33951 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),
33956 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33957 y : minY + (this.unitWidth + this.gutter) * 1
33965 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33970 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33971 y : minY + (this.unitWidth + this.gutter) * 2
33975 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33976 y : minY + (this.unitWidth + this.gutter) * 2
33980 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),
33981 y : minY + (this.unitWidth + this.gutter) * 2
33989 * remove a Masonry Brick
33990 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33992 removeBrick : function(brick_id)
33998 for (var i = 0; i<this.bricks.length; i++) {
33999 if (this.bricks[i].id == brick_id) {
34000 this.bricks.splice(i,1);
34001 this.el.dom.removeChild(Roo.get(brick_id).dom);
34008 * adds a Masonry Brick
34009 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34011 addBrick : function(cfg)
34013 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34014 //this.register(cn);
34015 cn.parentId = this.id;
34016 cn.render(this.el);
34021 * register a Masonry Brick
34022 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34025 register : function(brick)
34027 this.bricks.push(brick);
34028 brick.masonryId = this.id;
34032 * clear all the Masonry Brick
34034 clearAll : function()
34037 //this.getChildContainer().dom.innerHTML = "";
34038 this.el.dom.innerHTML = '';
34041 getSelected : function()
34043 if (!this.selectedBrick) {
34047 return this.selectedBrick;
34051 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34055 * register a Masonry Layout
34056 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34059 register : function(layout)
34061 this.groups[layout.id] = layout;
34064 * fetch a Masonry Layout based on the masonry layout ID
34065 * @param {string} the masonry layout to add
34066 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34069 get: function(layout_id) {
34070 if (typeof(this.groups[layout_id]) == 'undefined') {
34073 return this.groups[layout_id] ;
34085 * http://masonry.desandro.com
34087 * The idea is to render all the bricks based on vertical width...
34089 * The original code extends 'outlayer' - we might need to use that....
34095 * @class Roo.bootstrap.LayoutMasonryAuto
34096 * @extends Roo.bootstrap.Component
34097 * Bootstrap Layout Masonry class
34100 * Create a new Element
34101 * @param {Object} config The config object
34104 Roo.bootstrap.LayoutMasonryAuto = function(config){
34105 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34108 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34111 * @cfg {Boolean} isFitWidth - resize the width..
34113 isFitWidth : false, // options..
34115 * @cfg {Boolean} isOriginLeft = left align?
34117 isOriginLeft : true,
34119 * @cfg {Boolean} isOriginTop = top align?
34121 isOriginTop : false,
34123 * @cfg {Boolean} isLayoutInstant = no animation?
34125 isLayoutInstant : false, // needed?
34127 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34129 isResizingContainer : true,
34131 * @cfg {Number} columnWidth width of the columns
34137 * @cfg {Number} maxCols maximum number of columns
34142 * @cfg {Number} padHeight padding below box..
34148 * @cfg {Boolean} isAutoInitial defalut true
34151 isAutoInitial : true,
34157 initialColumnWidth : 0,
34158 currentSize : null,
34160 colYs : null, // array.
34167 bricks: null, //CompositeElement
34168 cols : 0, // array?
34169 // element : null, // wrapped now this.el
34170 _isLayoutInited : null,
34173 getAutoCreate : function(){
34177 cls: 'blog-masonary-wrapper ' + this.cls,
34179 cls : 'mas-boxes masonary'
34186 getChildContainer: function( )
34188 if (this.boxesEl) {
34189 return this.boxesEl;
34192 this.boxesEl = this.el.select('.mas-boxes').first();
34194 return this.boxesEl;
34198 initEvents : function()
34202 if(this.isAutoInitial){
34203 Roo.log('hook children rendered');
34204 this.on('childrenrendered', function() {
34205 Roo.log('children rendered');
34212 initial : function()
34214 this.reloadItems();
34216 this.currentSize = this.el.getBox(true);
34218 /// was window resize... - let's see if this works..
34219 Roo.EventManager.onWindowResize(this.resize, this);
34221 if(!this.isAutoInitial){
34226 this.layout.defer(500,this);
34229 reloadItems: function()
34231 this.bricks = this.el.select('.masonry-brick', true);
34233 this.bricks.each(function(b) {
34234 //Roo.log(b.getSize());
34235 if (!b.attr('originalwidth')) {
34236 b.attr('originalwidth', b.getSize().width);
34241 Roo.log(this.bricks.elements.length);
34244 resize : function()
34247 var cs = this.el.getBox(true);
34249 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34250 Roo.log("no change in with or X");
34253 this.currentSize = cs;
34257 layout : function()
34260 this._resetLayout();
34261 //this._manageStamps();
34263 // don't animate first layout
34264 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34265 this.layoutItems( isInstant );
34267 // flag for initalized
34268 this._isLayoutInited = true;
34271 layoutItems : function( isInstant )
34273 //var items = this._getItemsForLayout( this.items );
34274 // original code supports filtering layout items.. we just ignore it..
34276 this._layoutItems( this.bricks , isInstant );
34278 this._postLayout();
34280 _layoutItems : function ( items , isInstant)
34282 //this.fireEvent( 'layout', this, items );
34285 if ( !items || !items.elements.length ) {
34286 // no items, emit event with empty array
34291 items.each(function(item) {
34292 Roo.log("layout item");
34294 // get x/y object from method
34295 var position = this._getItemLayoutPosition( item );
34297 position.item = item;
34298 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34299 queue.push( position );
34302 this._processLayoutQueue( queue );
34304 /** Sets position of item in DOM
34305 * @param {Element} item
34306 * @param {Number} x - horizontal position
34307 * @param {Number} y - vertical position
34308 * @param {Boolean} isInstant - disables transitions
34310 _processLayoutQueue : function( queue )
34312 for ( var i=0, len = queue.length; i < len; i++ ) {
34313 var obj = queue[i];
34314 obj.item.position('absolute');
34315 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34321 * Any logic you want to do after each layout,
34322 * i.e. size the container
34324 _postLayout : function()
34326 this.resizeContainer();
34329 resizeContainer : function()
34331 if ( !this.isResizingContainer ) {
34334 var size = this._getContainerSize();
34336 this.el.setSize(size.width,size.height);
34337 this.boxesEl.setSize(size.width,size.height);
34343 _resetLayout : function()
34345 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34346 this.colWidth = this.el.getWidth();
34347 //this.gutter = this.el.getWidth();
34349 this.measureColumns();
34355 this.colYs.push( 0 );
34361 measureColumns : function()
34363 this.getContainerWidth();
34364 // if columnWidth is 0, default to outerWidth of first item
34365 if ( !this.columnWidth ) {
34366 var firstItem = this.bricks.first();
34367 Roo.log(firstItem);
34368 this.columnWidth = this.containerWidth;
34369 if (firstItem && firstItem.attr('originalwidth') ) {
34370 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34372 // columnWidth fall back to item of first element
34373 Roo.log("set column width?");
34374 this.initialColumnWidth = this.columnWidth ;
34376 // if first elem has no width, default to size of container
34381 if (this.initialColumnWidth) {
34382 this.columnWidth = this.initialColumnWidth;
34387 // column width is fixed at the top - however if container width get's smaller we should
34390 // this bit calcs how man columns..
34392 var columnWidth = this.columnWidth += this.gutter;
34394 // calculate columns
34395 var containerWidth = this.containerWidth + this.gutter;
34397 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34398 // fix rounding errors, typically with gutters
34399 var excess = columnWidth - containerWidth % columnWidth;
34402 // if overshoot is less than a pixel, round up, otherwise floor it
34403 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34404 cols = Math[ mathMethod ]( cols );
34405 this.cols = Math.max( cols, 1 );
34406 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34408 // padding positioning..
34409 var totalColWidth = this.cols * this.columnWidth;
34410 var padavail = this.containerWidth - totalColWidth;
34411 // so for 2 columns - we need 3 'pads'
34413 var padNeeded = (1+this.cols) * this.padWidth;
34415 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34417 this.columnWidth += padExtra
34418 //this.padWidth = Math.floor(padavail / ( this.cols));
34420 // adjust colum width so that padding is fixed??
34422 // we have 3 columns ... total = width * 3
34423 // we have X left over... that should be used by
34425 //if (this.expandC) {
34433 getContainerWidth : function()
34435 /* // container is parent if fit width
34436 var container = this.isFitWidth ? this.element.parentNode : this.element;
34437 // check that this.size and size are there
34438 // IE8 triggers resize on body size change, so they might not be
34440 var size = getSize( container ); //FIXME
34441 this.containerWidth = size && size.innerWidth; //FIXME
34444 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34448 _getItemLayoutPosition : function( item ) // what is item?
34450 // we resize the item to our columnWidth..
34452 item.setWidth(this.columnWidth);
34453 item.autoBoxAdjust = false;
34455 var sz = item.getSize();
34457 // how many columns does this brick span
34458 var remainder = this.containerWidth % this.columnWidth;
34460 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34461 // round if off by 1 pixel, otherwise use ceil
34462 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34463 colSpan = Math.min( colSpan, this.cols );
34465 // normally this should be '1' as we dont' currently allow multi width columns..
34467 var colGroup = this._getColGroup( colSpan );
34468 // get the minimum Y value from the columns
34469 var minimumY = Math.min.apply( Math, colGroup );
34470 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34472 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34474 // position the brick
34476 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34477 y: this.currentSize.y + minimumY + this.padHeight
34481 // apply setHeight to necessary columns
34482 var setHeight = minimumY + sz.height + this.padHeight;
34483 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34485 var setSpan = this.cols + 1 - colGroup.length;
34486 for ( var i = 0; i < setSpan; i++ ) {
34487 this.colYs[ shortColIndex + i ] = setHeight ;
34494 * @param {Number} colSpan - number of columns the element spans
34495 * @returns {Array} colGroup
34497 _getColGroup : function( colSpan )
34499 if ( colSpan < 2 ) {
34500 // if brick spans only one column, use all the column Ys
34505 // how many different places could this brick fit horizontally
34506 var groupCount = this.cols + 1 - colSpan;
34507 // for each group potential horizontal position
34508 for ( var i = 0; i < groupCount; i++ ) {
34509 // make an array of colY values for that one group
34510 var groupColYs = this.colYs.slice( i, i + colSpan );
34511 // and get the max value of the array
34512 colGroup[i] = Math.max.apply( Math, groupColYs );
34517 _manageStamp : function( stamp )
34519 var stampSize = stamp.getSize();
34520 var offset = stamp.getBox();
34521 // get the columns that this stamp affects
34522 var firstX = this.isOriginLeft ? offset.x : offset.right;
34523 var lastX = firstX + stampSize.width;
34524 var firstCol = Math.floor( firstX / this.columnWidth );
34525 firstCol = Math.max( 0, firstCol );
34527 var lastCol = Math.floor( lastX / this.columnWidth );
34528 // lastCol should not go over if multiple of columnWidth #425
34529 lastCol -= lastX % this.columnWidth ? 0 : 1;
34530 lastCol = Math.min( this.cols - 1, lastCol );
34532 // set colYs to bottom of the stamp
34533 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34536 for ( var i = firstCol; i <= lastCol; i++ ) {
34537 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34542 _getContainerSize : function()
34544 this.maxY = Math.max.apply( Math, this.colYs );
34549 if ( this.isFitWidth ) {
34550 size.width = this._getContainerFitWidth();
34556 _getContainerFitWidth : function()
34558 var unusedCols = 0;
34559 // count unused columns
34562 if ( this.colYs[i] !== 0 ) {
34567 // fit container to columns that have been used
34568 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34571 needsResizeLayout : function()
34573 var previousWidth = this.containerWidth;
34574 this.getContainerWidth();
34575 return previousWidth !== this.containerWidth;
34590 * @class Roo.bootstrap.MasonryBrick
34591 * @extends Roo.bootstrap.Component
34592 * Bootstrap MasonryBrick class
34595 * Create a new MasonryBrick
34596 * @param {Object} config The config object
34599 Roo.bootstrap.MasonryBrick = function(config){
34601 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34603 Roo.bootstrap.MasonryBrick.register(this);
34609 * When a MasonryBrick is clcik
34610 * @param {Roo.bootstrap.MasonryBrick} this
34611 * @param {Roo.EventObject} e
34617 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34620 * @cfg {String} title
34624 * @cfg {String} html
34628 * @cfg {String} bgimage
34632 * @cfg {String} videourl
34636 * @cfg {String} cls
34640 * @cfg {String} href
34644 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34649 * @cfg {String} placetitle (center|bottom)
34654 * @cfg {Boolean} isFitContainer defalut true
34656 isFitContainer : true,
34659 * @cfg {Boolean} preventDefault defalut false
34661 preventDefault : false,
34664 * @cfg {Boolean} inverse defalut false
34666 maskInverse : false,
34668 getAutoCreate : function()
34670 if(!this.isFitContainer){
34671 return this.getSplitAutoCreate();
34674 var cls = 'masonry-brick masonry-brick-full';
34676 if(this.href.length){
34677 cls += ' masonry-brick-link';
34680 if(this.bgimage.length){
34681 cls += ' masonry-brick-image';
34684 if(this.maskInverse){
34685 cls += ' mask-inverse';
34688 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34689 cls += ' enable-mask';
34693 cls += ' masonry-' + this.size + '-brick';
34696 if(this.placetitle.length){
34698 switch (this.placetitle) {
34700 cls += ' masonry-center-title';
34703 cls += ' masonry-bottom-title';
34710 if(!this.html.length && !this.bgimage.length){
34711 cls += ' masonry-center-title';
34714 if(!this.html.length && this.bgimage.length){
34715 cls += ' masonry-bottom-title';
34720 cls += ' ' + this.cls;
34724 tag: (this.href.length) ? 'a' : 'div',
34729 cls: 'masonry-brick-mask'
34733 cls: 'masonry-brick-paragraph',
34739 if(this.href.length){
34740 cfg.href = this.href;
34743 var cn = cfg.cn[1].cn;
34745 if(this.title.length){
34748 cls: 'masonry-brick-title',
34753 if(this.html.length){
34756 cls: 'masonry-brick-text',
34761 if (!this.title.length && !this.html.length) {
34762 cfg.cn[1].cls += ' hide';
34765 if(this.bgimage.length){
34768 cls: 'masonry-brick-image-view',
34773 if(this.videourl.length){
34774 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34775 // youtube support only?
34778 cls: 'masonry-brick-image-view',
34781 allowfullscreen : true
34789 getSplitAutoCreate : function()
34791 var cls = 'masonry-brick masonry-brick-split';
34793 if(this.href.length){
34794 cls += ' masonry-brick-link';
34797 if(this.bgimage.length){
34798 cls += ' masonry-brick-image';
34802 cls += ' masonry-' + this.size + '-brick';
34805 switch (this.placetitle) {
34807 cls += ' masonry-center-title';
34810 cls += ' masonry-bottom-title';
34813 if(!this.bgimage.length){
34814 cls += ' masonry-center-title';
34817 if(this.bgimage.length){
34818 cls += ' masonry-bottom-title';
34824 cls += ' ' + this.cls;
34828 tag: (this.href.length) ? 'a' : 'div',
34833 cls: 'masonry-brick-split-head',
34837 cls: 'masonry-brick-paragraph',
34844 cls: 'masonry-brick-split-body',
34850 if(this.href.length){
34851 cfg.href = this.href;
34854 if(this.title.length){
34855 cfg.cn[0].cn[0].cn.push({
34857 cls: 'masonry-brick-title',
34862 if(this.html.length){
34863 cfg.cn[1].cn.push({
34865 cls: 'masonry-brick-text',
34870 if(this.bgimage.length){
34871 cfg.cn[0].cn.push({
34873 cls: 'masonry-brick-image-view',
34878 if(this.videourl.length){
34879 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34880 // youtube support only?
34881 cfg.cn[0].cn.cn.push({
34883 cls: 'masonry-brick-image-view',
34886 allowfullscreen : true
34893 initEvents: function()
34895 switch (this.size) {
34928 this.el.on('touchstart', this.onTouchStart, this);
34929 this.el.on('touchmove', this.onTouchMove, this);
34930 this.el.on('touchend', this.onTouchEnd, this);
34931 this.el.on('contextmenu', this.onContextMenu, this);
34933 this.el.on('mouseenter' ,this.enter, this);
34934 this.el.on('mouseleave', this.leave, this);
34935 this.el.on('click', this.onClick, this);
34938 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34939 this.parent().bricks.push(this);
34944 onClick: function(e, el)
34946 var time = this.endTimer - this.startTimer;
34947 // Roo.log(e.preventDefault());
34950 e.preventDefault();
34955 if(!this.preventDefault){
34959 e.preventDefault();
34961 if (this.activeClass != '') {
34962 this.selectBrick();
34965 this.fireEvent('click', this, e);
34968 enter: function(e, el)
34970 e.preventDefault();
34972 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34976 if(this.bgimage.length && this.html.length){
34977 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34981 leave: function(e, el)
34983 e.preventDefault();
34985 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34989 if(this.bgimage.length && this.html.length){
34990 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34994 onTouchStart: function(e, el)
34996 // e.preventDefault();
34998 this.touchmoved = false;
35000 if(!this.isFitContainer){
35004 if(!this.bgimage.length || !this.html.length){
35008 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35010 this.timer = new Date().getTime();
35014 onTouchMove: function(e, el)
35016 this.touchmoved = true;
35019 onContextMenu : function(e,el)
35021 e.preventDefault();
35022 e.stopPropagation();
35026 onTouchEnd: function(e, el)
35028 // e.preventDefault();
35030 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35037 if(!this.bgimage.length || !this.html.length){
35039 if(this.href.length){
35040 window.location.href = this.href;
35046 if(!this.isFitContainer){
35050 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35052 window.location.href = this.href;
35055 //selection on single brick only
35056 selectBrick : function() {
35058 if (!this.parentId) {
35062 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35063 var index = m.selectedBrick.indexOf(this.id);
35066 m.selectedBrick.splice(index,1);
35067 this.el.removeClass(this.activeClass);
35071 for(var i = 0; i < m.selectedBrick.length; i++) {
35072 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35073 b.el.removeClass(b.activeClass);
35076 m.selectedBrick = [];
35078 m.selectedBrick.push(this.id);
35079 this.el.addClass(this.activeClass);
35083 isSelected : function(){
35084 return this.el.hasClass(this.activeClass);
35089 Roo.apply(Roo.bootstrap.MasonryBrick, {
35092 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35094 * register a Masonry Brick
35095 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35098 register : function(brick)
35100 //this.groups[brick.id] = brick;
35101 this.groups.add(brick.id, brick);
35104 * fetch a masonry brick based on the masonry brick ID
35105 * @param {string} the masonry brick to add
35106 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35109 get: function(brick_id)
35111 // if (typeof(this.groups[brick_id]) == 'undefined') {
35114 // return this.groups[brick_id] ;
35116 if(this.groups.key(brick_id)) {
35117 return this.groups.key(brick_id);
35135 * @class Roo.bootstrap.Brick
35136 * @extends Roo.bootstrap.Component
35137 * Bootstrap Brick class
35140 * Create a new Brick
35141 * @param {Object} config The config object
35144 Roo.bootstrap.Brick = function(config){
35145 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35151 * When a Brick is click
35152 * @param {Roo.bootstrap.Brick} this
35153 * @param {Roo.EventObject} e
35159 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35162 * @cfg {String} title
35166 * @cfg {String} html
35170 * @cfg {String} bgimage
35174 * @cfg {String} cls
35178 * @cfg {String} href
35182 * @cfg {String} video
35186 * @cfg {Boolean} square
35190 getAutoCreate : function()
35192 var cls = 'roo-brick';
35194 if(this.href.length){
35195 cls += ' roo-brick-link';
35198 if(this.bgimage.length){
35199 cls += ' roo-brick-image';
35202 if(!this.html.length && !this.bgimage.length){
35203 cls += ' roo-brick-center-title';
35206 if(!this.html.length && this.bgimage.length){
35207 cls += ' roo-brick-bottom-title';
35211 cls += ' ' + this.cls;
35215 tag: (this.href.length) ? 'a' : 'div',
35220 cls: 'roo-brick-paragraph',
35226 if(this.href.length){
35227 cfg.href = this.href;
35230 var cn = cfg.cn[0].cn;
35232 if(this.title.length){
35235 cls: 'roo-brick-title',
35240 if(this.html.length){
35243 cls: 'roo-brick-text',
35250 if(this.bgimage.length){
35253 cls: 'roo-brick-image-view',
35261 initEvents: function()
35263 if(this.title.length || this.html.length){
35264 this.el.on('mouseenter' ,this.enter, this);
35265 this.el.on('mouseleave', this.leave, this);
35268 Roo.EventManager.onWindowResize(this.resize, this);
35270 if(this.bgimage.length){
35271 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35272 this.imageEl.on('load', this.onImageLoad, this);
35279 onImageLoad : function()
35284 resize : function()
35286 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35288 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35290 if(this.bgimage.length){
35291 var image = this.el.select('.roo-brick-image-view', true).first();
35293 image.setWidth(paragraph.getWidth());
35296 image.setHeight(paragraph.getWidth());
35299 this.el.setHeight(image.getHeight());
35300 paragraph.setHeight(image.getHeight());
35306 enter: function(e, el)
35308 e.preventDefault();
35310 if(this.bgimage.length){
35311 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35312 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35316 leave: function(e, el)
35318 e.preventDefault();
35320 if(this.bgimage.length){
35321 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35322 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35337 * @class Roo.bootstrap.NumberField
35338 * @extends Roo.bootstrap.Input
35339 * Bootstrap NumberField class
35345 * Create a new NumberField
35346 * @param {Object} config The config object
35349 Roo.bootstrap.NumberField = function(config){
35350 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35353 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35356 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35358 allowDecimals : true,
35360 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35362 decimalSeparator : ".",
35364 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35366 decimalPrecision : 2,
35368 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35370 allowNegative : true,
35373 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35377 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35379 minValue : Number.NEGATIVE_INFINITY,
35381 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35383 maxValue : Number.MAX_VALUE,
35385 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35387 minText : "The minimum value for this field is {0}",
35389 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35391 maxText : "The maximum value for this field is {0}",
35393 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35394 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35396 nanText : "{0} is not a valid number",
35398 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35400 thousandsDelimiter : false,
35402 * @cfg {String} valueAlign alignment of value
35404 valueAlign : "left",
35406 getAutoCreate : function()
35408 var hiddenInput = {
35412 cls: 'hidden-number-input'
35416 hiddenInput.name = this.name;
35421 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35423 this.name = hiddenInput.name;
35425 if(cfg.cn.length > 0) {
35426 cfg.cn.push(hiddenInput);
35433 initEvents : function()
35435 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35437 var allowed = "0123456789";
35439 if(this.allowDecimals){
35440 allowed += this.decimalSeparator;
35443 if(this.allowNegative){
35447 if(this.thousandsDelimiter) {
35451 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35453 var keyPress = function(e){
35455 var k = e.getKey();
35457 var c = e.getCharCode();
35460 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35461 allowed.indexOf(String.fromCharCode(c)) === -1
35467 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35471 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35476 this.el.on("keypress", keyPress, this);
35479 validateValue : function(value)
35482 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35486 var num = this.parseValue(value);
35489 this.markInvalid(String.format(this.nanText, value));
35493 if(num < this.minValue){
35494 this.markInvalid(String.format(this.minText, this.minValue));
35498 if(num > this.maxValue){
35499 this.markInvalid(String.format(this.maxText, this.maxValue));
35506 getValue : function()
35508 var v = this.hiddenEl().getValue();
35510 return this.fixPrecision(this.parseValue(v));
35513 parseValue : function(value)
35515 if(this.thousandsDelimiter) {
35517 r = new RegExp(",", "g");
35518 value = value.replace(r, "");
35521 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35522 return isNaN(value) ? '' : value;
35525 fixPrecision : function(value)
35527 if(this.thousandsDelimiter) {
35529 r = new RegExp(",", "g");
35530 value = value.replace(r, "");
35533 var nan = isNaN(value);
35535 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35536 return nan ? '' : value;
35538 return parseFloat(value).toFixed(this.decimalPrecision);
35541 setValue : function(v)
35543 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35549 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35551 this.inputEl().dom.value = (v == '') ? '' :
35552 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35554 if(!this.allowZero && v === '0') {
35555 this.hiddenEl().dom.value = '';
35556 this.inputEl().dom.value = '';
35563 decimalPrecisionFcn : function(v)
35565 return Math.floor(v);
35568 beforeBlur : function()
35570 var v = this.parseValue(this.getRawValue());
35572 if(v || v === 0 || v === ''){
35577 hiddenEl : function()
35579 return this.el.select('input.hidden-number-input',true).first();
35591 * @class Roo.bootstrap.DocumentSlider
35592 * @extends Roo.bootstrap.Component
35593 * Bootstrap DocumentSlider class
35596 * Create a new DocumentViewer
35597 * @param {Object} config The config object
35600 Roo.bootstrap.DocumentSlider = function(config){
35601 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35608 * Fire after initEvent
35609 * @param {Roo.bootstrap.DocumentSlider} this
35614 * Fire after update
35615 * @param {Roo.bootstrap.DocumentSlider} this
35621 * @param {Roo.bootstrap.DocumentSlider} this
35627 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35633 getAutoCreate : function()
35637 cls : 'roo-document-slider',
35641 cls : 'roo-document-slider-header',
35645 cls : 'roo-document-slider-header-title'
35651 cls : 'roo-document-slider-body',
35655 cls : 'roo-document-slider-prev',
35659 cls : 'fa fa-chevron-left'
35665 cls : 'roo-document-slider-thumb',
35669 cls : 'roo-document-slider-image'
35675 cls : 'roo-document-slider-next',
35679 cls : 'fa fa-chevron-right'
35691 initEvents : function()
35693 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35694 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35696 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35697 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35699 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35700 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35702 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35703 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35705 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35706 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35708 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35709 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35711 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35712 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35714 this.thumbEl.on('click', this.onClick, this);
35716 this.prevIndicator.on('click', this.prev, this);
35718 this.nextIndicator.on('click', this.next, this);
35722 initial : function()
35724 if(this.files.length){
35725 this.indicator = 1;
35729 this.fireEvent('initial', this);
35732 update : function()
35734 this.imageEl.attr('src', this.files[this.indicator - 1]);
35736 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35738 this.prevIndicator.show();
35740 if(this.indicator == 1){
35741 this.prevIndicator.hide();
35744 this.nextIndicator.show();
35746 if(this.indicator == this.files.length){
35747 this.nextIndicator.hide();
35750 this.thumbEl.scrollTo('top');
35752 this.fireEvent('update', this);
35755 onClick : function(e)
35757 e.preventDefault();
35759 this.fireEvent('click', this);
35764 e.preventDefault();
35766 this.indicator = Math.max(1, this.indicator - 1);
35773 e.preventDefault();
35775 this.indicator = Math.min(this.files.length, this.indicator + 1);
35789 * @class Roo.bootstrap.RadioSet
35790 * @extends Roo.bootstrap.Input
35791 * Bootstrap RadioSet class
35792 * @cfg {String} indicatorpos (left|right) default left
35793 * @cfg {Boolean} inline (true|false) inline the element (default true)
35794 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35796 * Create a new RadioSet
35797 * @param {Object} config The config object
35800 Roo.bootstrap.RadioSet = function(config){
35802 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35806 Roo.bootstrap.RadioSet.register(this);
35811 * Fires when the element is checked or unchecked.
35812 * @param {Roo.bootstrap.RadioSet} this This radio
35813 * @param {Roo.bootstrap.Radio} item The checked item
35818 * Fires when the element is click.
35819 * @param {Roo.bootstrap.RadioSet} this This radio set
35820 * @param {Roo.bootstrap.Radio} item The checked item
35821 * @param {Roo.EventObject} e The event object
35828 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
35836 indicatorpos : 'left',
35838 getAutoCreate : function()
35842 cls : 'roo-radio-set-label',
35846 html : this.fieldLabel
35850 if (Roo.bootstrap.version == 3) {
35853 if(this.indicatorpos == 'left'){
35856 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35857 tooltip : 'This field is required'
35862 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35863 tooltip : 'This field is required'
35869 cls : 'roo-radio-set-items'
35872 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35874 if (align === 'left' && this.fieldLabel.length) {
35877 cls : "roo-radio-set-right",
35883 if(this.labelWidth > 12){
35884 label.style = "width: " + this.labelWidth + 'px';
35887 if(this.labelWidth < 13 && this.labelmd == 0){
35888 this.labelmd = this.labelWidth;
35891 if(this.labellg > 0){
35892 label.cls += ' col-lg-' + this.labellg;
35893 items.cls += ' col-lg-' + (12 - this.labellg);
35896 if(this.labelmd > 0){
35897 label.cls += ' col-md-' + this.labelmd;
35898 items.cls += ' col-md-' + (12 - this.labelmd);
35901 if(this.labelsm > 0){
35902 label.cls += ' col-sm-' + this.labelsm;
35903 items.cls += ' col-sm-' + (12 - this.labelsm);
35906 if(this.labelxs > 0){
35907 label.cls += ' col-xs-' + this.labelxs;
35908 items.cls += ' col-xs-' + (12 - this.labelxs);
35914 cls : 'roo-radio-set',
35918 cls : 'roo-radio-set-input',
35921 value : this.value ? this.value : ''
35928 if(this.weight.length){
35929 cfg.cls += ' roo-radio-' + this.weight;
35933 cfg.cls += ' roo-radio-set-inline';
35937 ['xs','sm','md','lg'].map(function(size){
35938 if (settings[size]) {
35939 cfg.cls += ' col-' + size + '-' + settings[size];
35947 initEvents : function()
35949 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35950 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35952 if(!this.fieldLabel.length){
35953 this.labelEl.hide();
35956 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35957 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35959 this.indicator = this.indicatorEl();
35961 if(this.indicator){
35962 this.indicator.addClass('invisible');
35965 this.originalValue = this.getValue();
35969 inputEl: function ()
35971 return this.el.select('.roo-radio-set-input', true).first();
35974 getChildContainer : function()
35976 return this.itemsEl;
35979 register : function(item)
35981 this.radioes.push(item);
35985 validate : function()
35987 if(this.getVisibilityEl().hasClass('hidden')){
35993 Roo.each(this.radioes, function(i){
36002 if(this.allowBlank) {
36006 if(this.disabled || valid){
36011 this.markInvalid();
36016 markValid : function()
36018 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36019 this.indicatorEl().removeClass('visible');
36020 this.indicatorEl().addClass('invisible');
36024 if (Roo.bootstrap.version == 3) {
36025 this.el.removeClass([this.invalidClass, this.validClass]);
36026 this.el.addClass(this.validClass);
36028 this.el.removeClass(['is-invalid','is-valid']);
36029 this.el.addClass(['is-valid']);
36031 this.fireEvent('valid', this);
36034 markInvalid : function(msg)
36036 if(this.allowBlank || this.disabled){
36040 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36041 this.indicatorEl().removeClass('invisible');
36042 this.indicatorEl().addClass('visible');
36044 if (Roo.bootstrap.version == 3) {
36045 this.el.removeClass([this.invalidClass, this.validClass]);
36046 this.el.addClass(this.invalidClass);
36048 this.el.removeClass(['is-invalid','is-valid']);
36049 this.el.addClass(['is-invalid']);
36052 this.fireEvent('invalid', this, msg);
36056 setValue : function(v, suppressEvent)
36058 if(this.value === v){
36065 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36068 Roo.each(this.radioes, function(i){
36070 i.el.removeClass('checked');
36073 Roo.each(this.radioes, function(i){
36075 if(i.value === v || i.value.toString() === v.toString()){
36077 i.el.addClass('checked');
36079 if(suppressEvent !== true){
36080 this.fireEvent('check', this, i);
36091 clearInvalid : function(){
36093 if(!this.el || this.preventMark){
36097 this.el.removeClass([this.invalidClass]);
36099 this.fireEvent('valid', this);
36104 Roo.apply(Roo.bootstrap.RadioSet, {
36108 register : function(set)
36110 this.groups[set.name] = set;
36113 get: function(name)
36115 if (typeof(this.groups[name]) == 'undefined') {
36119 return this.groups[name] ;
36125 * Ext JS Library 1.1.1
36126 * Copyright(c) 2006-2007, Ext JS, LLC.
36128 * Originally Released Under LGPL - original licence link has changed is not relivant.
36131 * <script type="text/javascript">
36136 * @class Roo.bootstrap.SplitBar
36137 * @extends Roo.util.Observable
36138 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36142 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36143 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36144 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36145 split.minSize = 100;
36146 split.maxSize = 600;
36147 split.animate = true;
36148 split.on('moved', splitterMoved);
36151 * Create a new SplitBar
36152 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36153 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36154 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36155 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36156 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36157 position of the SplitBar).
36159 Roo.bootstrap.SplitBar = function(cfg){
36164 // dragElement : elm
36165 // resizingElement: el,
36167 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36168 // placement : Roo.bootstrap.SplitBar.LEFT ,
36169 // existingProxy ???
36172 this.el = Roo.get(cfg.dragElement, true);
36173 this.el.dom.unselectable = "on";
36175 this.resizingEl = Roo.get(cfg.resizingElement, true);
36179 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36180 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36183 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36186 * The minimum size of the resizing element. (Defaults to 0)
36192 * The maximum size of the resizing element. (Defaults to 2000)
36195 this.maxSize = 2000;
36198 * Whether to animate the transition to the new size
36201 this.animate = false;
36204 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36207 this.useShim = false;
36212 if(!cfg.existingProxy){
36214 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36216 this.proxy = Roo.get(cfg.existingProxy).dom;
36219 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36222 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36225 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36228 this.dragSpecs = {};
36231 * @private The adapter to use to positon and resize elements
36233 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36234 this.adapter.init(this);
36236 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36238 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36239 this.el.addClass("roo-splitbar-h");
36242 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36243 this.el.addClass("roo-splitbar-v");
36249 * Fires when the splitter is moved (alias for {@link #event-moved})
36250 * @param {Roo.bootstrap.SplitBar} this
36251 * @param {Number} newSize the new width or height
36256 * Fires when the splitter is moved
36257 * @param {Roo.bootstrap.SplitBar} this
36258 * @param {Number} newSize the new width or height
36262 * @event beforeresize
36263 * Fires before the splitter is dragged
36264 * @param {Roo.bootstrap.SplitBar} this
36266 "beforeresize" : true,
36268 "beforeapply" : true
36271 Roo.util.Observable.call(this);
36274 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36275 onStartProxyDrag : function(x, y){
36276 this.fireEvent("beforeresize", this);
36278 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36280 o.enableDisplayMode("block");
36281 // all splitbars share the same overlay
36282 Roo.bootstrap.SplitBar.prototype.overlay = o;
36284 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36285 this.overlay.show();
36286 Roo.get(this.proxy).setDisplayed("block");
36287 var size = this.adapter.getElementSize(this);
36288 this.activeMinSize = this.getMinimumSize();;
36289 this.activeMaxSize = this.getMaximumSize();;
36290 var c1 = size - this.activeMinSize;
36291 var c2 = Math.max(this.activeMaxSize - size, 0);
36292 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36293 this.dd.resetConstraints();
36294 this.dd.setXConstraint(
36295 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36296 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36298 this.dd.setYConstraint(0, 0);
36300 this.dd.resetConstraints();
36301 this.dd.setXConstraint(0, 0);
36302 this.dd.setYConstraint(
36303 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36304 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36307 this.dragSpecs.startSize = size;
36308 this.dragSpecs.startPoint = [x, y];
36309 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36313 * @private Called after the drag operation by the DDProxy
36315 onEndProxyDrag : function(e){
36316 Roo.get(this.proxy).setDisplayed(false);
36317 var endPoint = Roo.lib.Event.getXY(e);
36319 this.overlay.hide();
36322 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36323 newSize = this.dragSpecs.startSize +
36324 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36325 endPoint[0] - this.dragSpecs.startPoint[0] :
36326 this.dragSpecs.startPoint[0] - endPoint[0]
36329 newSize = this.dragSpecs.startSize +
36330 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36331 endPoint[1] - this.dragSpecs.startPoint[1] :
36332 this.dragSpecs.startPoint[1] - endPoint[1]
36335 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36336 if(newSize != this.dragSpecs.startSize){
36337 if(this.fireEvent('beforeapply', this, newSize) !== false){
36338 this.adapter.setElementSize(this, newSize);
36339 this.fireEvent("moved", this, newSize);
36340 this.fireEvent("resize", this, newSize);
36346 * Get the adapter this SplitBar uses
36347 * @return The adapter object
36349 getAdapter : function(){
36350 return this.adapter;
36354 * Set the adapter this SplitBar uses
36355 * @param {Object} adapter A SplitBar adapter object
36357 setAdapter : function(adapter){
36358 this.adapter = adapter;
36359 this.adapter.init(this);
36363 * Gets the minimum size for the resizing element
36364 * @return {Number} The minimum size
36366 getMinimumSize : function(){
36367 return this.minSize;
36371 * Sets the minimum size for the resizing element
36372 * @param {Number} minSize The minimum size
36374 setMinimumSize : function(minSize){
36375 this.minSize = minSize;
36379 * Gets the maximum size for the resizing element
36380 * @return {Number} The maximum size
36382 getMaximumSize : function(){
36383 return this.maxSize;
36387 * Sets the maximum size for the resizing element
36388 * @param {Number} maxSize The maximum size
36390 setMaximumSize : function(maxSize){
36391 this.maxSize = maxSize;
36395 * Sets the initialize size for the resizing element
36396 * @param {Number} size The initial size
36398 setCurrentSize : function(size){
36399 var oldAnimate = this.animate;
36400 this.animate = false;
36401 this.adapter.setElementSize(this, size);
36402 this.animate = oldAnimate;
36406 * Destroy this splitbar.
36407 * @param {Boolean} removeEl True to remove the element
36409 destroy : function(removeEl){
36411 this.shim.remove();
36414 this.proxy.parentNode.removeChild(this.proxy);
36422 * @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.
36424 Roo.bootstrap.SplitBar.createProxy = function(dir){
36425 var proxy = new Roo.Element(document.createElement("div"));
36426 proxy.unselectable();
36427 var cls = 'roo-splitbar-proxy';
36428 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36429 document.body.appendChild(proxy.dom);
36434 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36435 * Default Adapter. It assumes the splitter and resizing element are not positioned
36436 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36438 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36441 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36442 // do nothing for now
36443 init : function(s){
36447 * Called before drag operations to get the current size of the resizing element.
36448 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36450 getElementSize : function(s){
36451 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36452 return s.resizingEl.getWidth();
36454 return s.resizingEl.getHeight();
36459 * Called after drag operations to set the size of the resizing element.
36460 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36461 * @param {Number} newSize The new size to set
36462 * @param {Function} onComplete A function to be invoked when resizing is complete
36464 setElementSize : function(s, newSize, onComplete){
36465 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36467 s.resizingEl.setWidth(newSize);
36469 onComplete(s, newSize);
36472 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36477 s.resizingEl.setHeight(newSize);
36479 onComplete(s, newSize);
36482 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36489 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36490 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36491 * Adapter that moves the splitter element to align with the resized sizing element.
36492 * Used with an absolute positioned SplitBar.
36493 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36494 * document.body, make sure you assign an id to the body element.
36496 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36497 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36498 this.container = Roo.get(container);
36501 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36502 init : function(s){
36503 this.basic.init(s);
36506 getElementSize : function(s){
36507 return this.basic.getElementSize(s);
36510 setElementSize : function(s, newSize, onComplete){
36511 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36514 moveSplitter : function(s){
36515 var yes = Roo.bootstrap.SplitBar;
36516 switch(s.placement){
36518 s.el.setX(s.resizingEl.getRight());
36521 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36524 s.el.setY(s.resizingEl.getBottom());
36527 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36534 * Orientation constant - Create a vertical SplitBar
36538 Roo.bootstrap.SplitBar.VERTICAL = 1;
36541 * Orientation constant - Create a horizontal SplitBar
36545 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36548 * Placement constant - The resizing element is to the left of the splitter element
36552 Roo.bootstrap.SplitBar.LEFT = 1;
36555 * Placement constant - The resizing element is to the right of the splitter element
36559 Roo.bootstrap.SplitBar.RIGHT = 2;
36562 * Placement constant - The resizing element is positioned above the splitter element
36566 Roo.bootstrap.SplitBar.TOP = 3;
36569 * Placement constant - The resizing element is positioned under splitter element
36573 Roo.bootstrap.SplitBar.BOTTOM = 4;
36574 Roo.namespace("Roo.bootstrap.layout");/*
36576 * Ext JS Library 1.1.1
36577 * Copyright(c) 2006-2007, Ext JS, LLC.
36579 * Originally Released Under LGPL - original licence link has changed is not relivant.
36582 * <script type="text/javascript">
36586 * @class Roo.bootstrap.layout.Manager
36587 * @extends Roo.bootstrap.Component
36588 * Base class for layout managers.
36590 Roo.bootstrap.layout.Manager = function(config)
36592 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36598 /** false to disable window resize monitoring @type Boolean */
36599 this.monitorWindowResize = true;
36604 * Fires when a layout is performed.
36605 * @param {Roo.LayoutManager} this
36609 * @event regionresized
36610 * Fires when the user resizes a region.
36611 * @param {Roo.LayoutRegion} region The resized region
36612 * @param {Number} newSize The new size (width for east/west, height for north/south)
36614 "regionresized" : true,
36616 * @event regioncollapsed
36617 * Fires when a region is collapsed.
36618 * @param {Roo.LayoutRegion} region The collapsed region
36620 "regioncollapsed" : true,
36622 * @event regionexpanded
36623 * Fires when a region is expanded.
36624 * @param {Roo.LayoutRegion} region The expanded region
36626 "regionexpanded" : true
36628 this.updating = false;
36631 this.el = Roo.get(config.el);
36637 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36642 monitorWindowResize : true,
36648 onRender : function(ct, position)
36651 this.el = Roo.get(ct);
36654 //this.fireEvent('render',this);
36658 initEvents: function()
36662 // ie scrollbar fix
36663 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36664 document.body.scroll = "no";
36665 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36666 this.el.position('relative');
36668 this.id = this.el.id;
36669 this.el.addClass("roo-layout-container");
36670 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36671 if(this.el.dom != document.body ) {
36672 this.el.on('resize', this.layout,this);
36673 this.el.on('show', this.layout,this);
36679 * Returns true if this layout is currently being updated
36680 * @return {Boolean}
36682 isUpdating : function(){
36683 return this.updating;
36687 * Suspend the LayoutManager from doing auto-layouts while
36688 * making multiple add or remove calls
36690 beginUpdate : function(){
36691 this.updating = true;
36695 * Restore auto-layouts and optionally disable the manager from performing a layout
36696 * @param {Boolean} noLayout true to disable a layout update
36698 endUpdate : function(noLayout){
36699 this.updating = false;
36705 layout: function(){
36709 onRegionResized : function(region, newSize){
36710 this.fireEvent("regionresized", region, newSize);
36714 onRegionCollapsed : function(region){
36715 this.fireEvent("regioncollapsed", region);
36718 onRegionExpanded : function(region){
36719 this.fireEvent("regionexpanded", region);
36723 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36724 * performs box-model adjustments.
36725 * @return {Object} The size as an object {width: (the width), height: (the height)}
36727 getViewSize : function()
36730 if(this.el.dom != document.body){
36731 size = this.el.getSize();
36733 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36735 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36736 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36741 * Returns the Element this layout is bound to.
36742 * @return {Roo.Element}
36744 getEl : function(){
36749 * Returns the specified region.
36750 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36751 * @return {Roo.LayoutRegion}
36753 getRegion : function(target){
36754 return this.regions[target.toLowerCase()];
36757 onWindowResize : function(){
36758 if(this.monitorWindowResize){
36765 * Ext JS Library 1.1.1
36766 * Copyright(c) 2006-2007, Ext JS, LLC.
36768 * Originally Released Under LGPL - original licence link has changed is not relivant.
36771 * <script type="text/javascript">
36774 * @class Roo.bootstrap.layout.Border
36775 * @extends Roo.bootstrap.layout.Manager
36776 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36777 * please see: examples/bootstrap/nested.html<br><br>
36779 <b>The container the layout is rendered into can be either the body element or any other element.
36780 If it is not the body element, the container needs to either be an absolute positioned element,
36781 or you will need to add "position:relative" to the css of the container. You will also need to specify
36782 the container size if it is not the body element.</b>
36785 * Create a new Border
36786 * @param {Object} config Configuration options
36788 Roo.bootstrap.layout.Border = function(config){
36789 config = config || {};
36790 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36794 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36795 if(config[region]){
36796 config[region].region = region;
36797 this.addRegion(config[region]);
36803 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
36805 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36807 parent : false, // this might point to a 'nest' or a ???
36810 * Creates and adds a new region if it doesn't already exist.
36811 * @param {String} target The target region key (north, south, east, west or center).
36812 * @param {Object} config The regions config object
36813 * @return {BorderLayoutRegion} The new region
36815 addRegion : function(config)
36817 if(!this.regions[config.region]){
36818 var r = this.factory(config);
36819 this.bindRegion(r);
36821 return this.regions[config.region];
36825 bindRegion : function(r){
36826 this.regions[r.config.region] = r;
36828 r.on("visibilitychange", this.layout, this);
36829 r.on("paneladded", this.layout, this);
36830 r.on("panelremoved", this.layout, this);
36831 r.on("invalidated", this.layout, this);
36832 r.on("resized", this.onRegionResized, this);
36833 r.on("collapsed", this.onRegionCollapsed, this);
36834 r.on("expanded", this.onRegionExpanded, this);
36838 * Performs a layout update.
36840 layout : function()
36842 if(this.updating) {
36846 // render all the rebions if they have not been done alreayd?
36847 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36848 if(this.regions[region] && !this.regions[region].bodyEl){
36849 this.regions[region].onRender(this.el)
36853 var size = this.getViewSize();
36854 var w = size.width;
36855 var h = size.height;
36860 //var x = 0, y = 0;
36862 var rs = this.regions;
36863 var north = rs["north"];
36864 var south = rs["south"];
36865 var west = rs["west"];
36866 var east = rs["east"];
36867 var center = rs["center"];
36868 //if(this.hideOnLayout){ // not supported anymore
36869 //c.el.setStyle("display", "none");
36871 if(north && north.isVisible()){
36872 var b = north.getBox();
36873 var m = north.getMargins();
36874 b.width = w - (m.left+m.right);
36877 centerY = b.height + b.y + m.bottom;
36878 centerH -= centerY;
36879 north.updateBox(this.safeBox(b));
36881 if(south && south.isVisible()){
36882 var b = south.getBox();
36883 var m = south.getMargins();
36884 b.width = w - (m.left+m.right);
36886 var totalHeight = (b.height + m.top + m.bottom);
36887 b.y = h - totalHeight + m.top;
36888 centerH -= totalHeight;
36889 south.updateBox(this.safeBox(b));
36891 if(west && west.isVisible()){
36892 var b = west.getBox();
36893 var m = west.getMargins();
36894 b.height = centerH - (m.top+m.bottom);
36896 b.y = centerY + m.top;
36897 var totalWidth = (b.width + m.left + m.right);
36898 centerX += totalWidth;
36899 centerW -= totalWidth;
36900 west.updateBox(this.safeBox(b));
36902 if(east && east.isVisible()){
36903 var b = east.getBox();
36904 var m = east.getMargins();
36905 b.height = centerH - (m.top+m.bottom);
36906 var totalWidth = (b.width + m.left + m.right);
36907 b.x = w - totalWidth + m.left;
36908 b.y = centerY + m.top;
36909 centerW -= totalWidth;
36910 east.updateBox(this.safeBox(b));
36913 var m = center.getMargins();
36915 x: centerX + m.left,
36916 y: centerY + m.top,
36917 width: centerW - (m.left+m.right),
36918 height: centerH - (m.top+m.bottom)
36920 //if(this.hideOnLayout){
36921 //center.el.setStyle("display", "block");
36923 center.updateBox(this.safeBox(centerBox));
36926 this.fireEvent("layout", this);
36930 safeBox : function(box){
36931 box.width = Math.max(0, box.width);
36932 box.height = Math.max(0, box.height);
36937 * Adds a ContentPanel (or subclass) to this layout.
36938 * @param {String} target The target region key (north, south, east, west or center).
36939 * @param {Roo.ContentPanel} panel The panel to add
36940 * @return {Roo.ContentPanel} The added panel
36942 add : function(target, panel){
36944 target = target.toLowerCase();
36945 return this.regions[target].add(panel);
36949 * Remove a ContentPanel (or subclass) to this layout.
36950 * @param {String} target The target region key (north, south, east, west or center).
36951 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36952 * @return {Roo.ContentPanel} The removed panel
36954 remove : function(target, panel){
36955 target = target.toLowerCase();
36956 return this.regions[target].remove(panel);
36960 * Searches all regions for a panel with the specified id
36961 * @param {String} panelId
36962 * @return {Roo.ContentPanel} The panel or null if it wasn't found
36964 findPanel : function(panelId){
36965 var rs = this.regions;
36966 for(var target in rs){
36967 if(typeof rs[target] != "function"){
36968 var p = rs[target].getPanel(panelId);
36978 * Searches all regions for a panel with the specified id and activates (shows) it.
36979 * @param {String/ContentPanel} panelId The panels id or the panel itself
36980 * @return {Roo.ContentPanel} The shown panel or null
36982 showPanel : function(panelId) {
36983 var rs = this.regions;
36984 for(var target in rs){
36985 var r = rs[target];
36986 if(typeof r != "function"){
36987 if(r.hasPanel(panelId)){
36988 return r.showPanel(panelId);
36996 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36997 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37000 restoreState : function(provider){
37002 provider = Roo.state.Manager;
37004 var sm = new Roo.LayoutStateManager();
37005 sm.init(this, provider);
37011 * Adds a xtype elements to the layout.
37015 xtype : 'ContentPanel',
37022 xtype : 'NestedLayoutPanel',
37028 items : [ ... list of content panels or nested layout panels.. ]
37032 * @param {Object} cfg Xtype definition of item to add.
37034 addxtype : function(cfg)
37036 // basically accepts a pannel...
37037 // can accept a layout region..!?!?
37038 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37041 // theory? children can only be panels??
37043 //if (!cfg.xtype.match(/Panel$/)) {
37048 if (typeof(cfg.region) == 'undefined') {
37049 Roo.log("Failed to add Panel, region was not set");
37053 var region = cfg.region;
37059 xitems = cfg.items;
37064 if ( region == 'center') {
37065 Roo.log("Center: " + cfg.title);
37071 case 'Content': // ContentPanel (el, cfg)
37072 case 'Scroll': // ContentPanel (el, cfg)
37074 cfg.autoCreate = cfg.autoCreate || true;
37075 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37077 // var el = this.el.createChild();
37078 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37081 this.add(region, ret);
37085 case 'TreePanel': // our new panel!
37086 cfg.el = this.el.createChild();
37087 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37088 this.add(region, ret);
37093 // create a new Layout (which is a Border Layout...
37095 var clayout = cfg.layout;
37096 clayout.el = this.el.createChild();
37097 clayout.items = clayout.items || [];
37101 // replace this exitems with the clayout ones..
37102 xitems = clayout.items;
37104 // force background off if it's in center...
37105 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37106 cfg.background = false;
37108 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37111 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37112 //console.log('adding nested layout panel ' + cfg.toSource());
37113 this.add(region, ret);
37114 nb = {}; /// find first...
37119 // needs grid and region
37121 //var el = this.getRegion(region).el.createChild();
37123 *var el = this.el.createChild();
37124 // create the grid first...
37125 cfg.grid.container = el;
37126 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37129 if (region == 'center' && this.active ) {
37130 cfg.background = false;
37133 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37135 this.add(region, ret);
37137 if (cfg.background) {
37138 // render grid on panel activation (if panel background)
37139 ret.on('activate', function(gp) {
37140 if (!gp.grid.rendered) {
37141 // gp.grid.render(el);
37145 // cfg.grid.render(el);
37151 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37152 // it was the old xcomponent building that caused this before.
37153 // espeically if border is the top element in the tree.
37163 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37165 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37166 this.add(region, ret);
37170 throw "Can not add '" + cfg.xtype + "' to Border";
37176 this.beginUpdate();
37180 Roo.each(xitems, function(i) {
37181 region = nb && i.region ? i.region : false;
37183 var add = ret.addxtype(i);
37186 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37187 if (!i.background) {
37188 abn[region] = nb[region] ;
37195 // make the last non-background panel active..
37196 //if (nb) { Roo.log(abn); }
37199 for(var r in abn) {
37200 region = this.getRegion(r);
37202 // tried using nb[r], but it does not work..
37204 region.showPanel(abn[r]);
37215 factory : function(cfg)
37218 var validRegions = Roo.bootstrap.layout.Border.regions;
37220 var target = cfg.region;
37223 var r = Roo.bootstrap.layout;
37227 return new r.North(cfg);
37229 return new r.South(cfg);
37231 return new r.East(cfg);
37233 return new r.West(cfg);
37235 return new r.Center(cfg);
37237 throw 'Layout region "'+target+'" not supported.';
37244 * Ext JS Library 1.1.1
37245 * Copyright(c) 2006-2007, Ext JS, LLC.
37247 * Originally Released Under LGPL - original licence link has changed is not relivant.
37250 * <script type="text/javascript">
37254 * @class Roo.bootstrap.layout.Basic
37255 * @extends Roo.util.Observable
37256 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37257 * and does not have a titlebar, tabs or any other features. All it does is size and position
37258 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37259 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37260 * @cfg {string} region the region that it inhabits..
37261 * @cfg {bool} skipConfig skip config?
37265 Roo.bootstrap.layout.Basic = function(config){
37267 this.mgr = config.mgr;
37269 this.position = config.region;
37271 var skipConfig = config.skipConfig;
37275 * @scope Roo.BasicLayoutRegion
37279 * @event beforeremove
37280 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37281 * @param {Roo.LayoutRegion} this
37282 * @param {Roo.ContentPanel} panel The panel
37283 * @param {Object} e The cancel event object
37285 "beforeremove" : true,
37287 * @event invalidated
37288 * Fires when the layout for this region is changed.
37289 * @param {Roo.LayoutRegion} this
37291 "invalidated" : true,
37293 * @event visibilitychange
37294 * Fires when this region is shown or hidden
37295 * @param {Roo.LayoutRegion} this
37296 * @param {Boolean} visibility true or false
37298 "visibilitychange" : true,
37300 * @event paneladded
37301 * Fires when a panel is added.
37302 * @param {Roo.LayoutRegion} this
37303 * @param {Roo.ContentPanel} panel The panel
37305 "paneladded" : true,
37307 * @event panelremoved
37308 * Fires when a panel is removed.
37309 * @param {Roo.LayoutRegion} this
37310 * @param {Roo.ContentPanel} panel The panel
37312 "panelremoved" : true,
37314 * @event beforecollapse
37315 * Fires when this region before collapse.
37316 * @param {Roo.LayoutRegion} this
37318 "beforecollapse" : true,
37321 * Fires when this region is collapsed.
37322 * @param {Roo.LayoutRegion} this
37324 "collapsed" : true,
37327 * Fires when this region is expanded.
37328 * @param {Roo.LayoutRegion} this
37333 * Fires when this region is slid into view.
37334 * @param {Roo.LayoutRegion} this
37336 "slideshow" : true,
37339 * Fires when this region slides out of view.
37340 * @param {Roo.LayoutRegion} this
37342 "slidehide" : true,
37344 * @event panelactivated
37345 * Fires when a panel is activated.
37346 * @param {Roo.LayoutRegion} this
37347 * @param {Roo.ContentPanel} panel The activated panel
37349 "panelactivated" : true,
37352 * Fires when the user resizes this region.
37353 * @param {Roo.LayoutRegion} this
37354 * @param {Number} newSize The new size (width for east/west, height for north/south)
37358 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37359 this.panels = new Roo.util.MixedCollection();
37360 this.panels.getKey = this.getPanelId.createDelegate(this);
37362 this.activePanel = null;
37363 // ensure listeners are added...
37365 if (config.listeners || config.events) {
37366 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37367 listeners : config.listeners || {},
37368 events : config.events || {}
37372 if(skipConfig !== true){
37373 this.applyConfig(config);
37377 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37379 getPanelId : function(p){
37383 applyConfig : function(config){
37384 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37385 this.config = config;
37390 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37391 * the width, for horizontal (north, south) the height.
37392 * @param {Number} newSize The new width or height
37394 resizeTo : function(newSize){
37395 var el = this.el ? this.el :
37396 (this.activePanel ? this.activePanel.getEl() : null);
37398 switch(this.position){
37401 el.setWidth(newSize);
37402 this.fireEvent("resized", this, newSize);
37406 el.setHeight(newSize);
37407 this.fireEvent("resized", this, newSize);
37413 getBox : function(){
37414 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37417 getMargins : function(){
37418 return this.margins;
37421 updateBox : function(box){
37423 var el = this.activePanel.getEl();
37424 el.dom.style.left = box.x + "px";
37425 el.dom.style.top = box.y + "px";
37426 this.activePanel.setSize(box.width, box.height);
37430 * Returns the container element for this region.
37431 * @return {Roo.Element}
37433 getEl : function(){
37434 return this.activePanel;
37438 * Returns true if this region is currently visible.
37439 * @return {Boolean}
37441 isVisible : function(){
37442 return this.activePanel ? true : false;
37445 setActivePanel : function(panel){
37446 panel = this.getPanel(panel);
37447 if(this.activePanel && this.activePanel != panel){
37448 this.activePanel.setActiveState(false);
37449 this.activePanel.getEl().setLeftTop(-10000,-10000);
37451 this.activePanel = panel;
37452 panel.setActiveState(true);
37454 panel.setSize(this.box.width, this.box.height);
37456 this.fireEvent("panelactivated", this, panel);
37457 this.fireEvent("invalidated");
37461 * Show the specified panel.
37462 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37463 * @return {Roo.ContentPanel} The shown panel or null
37465 showPanel : function(panel){
37466 panel = this.getPanel(panel);
37468 this.setActivePanel(panel);
37474 * Get the active panel for this region.
37475 * @return {Roo.ContentPanel} The active panel or null
37477 getActivePanel : function(){
37478 return this.activePanel;
37482 * Add the passed ContentPanel(s)
37483 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37484 * @return {Roo.ContentPanel} The panel added (if only one was added)
37486 add : function(panel){
37487 if(arguments.length > 1){
37488 for(var i = 0, len = arguments.length; i < len; i++) {
37489 this.add(arguments[i]);
37493 if(this.hasPanel(panel)){
37494 this.showPanel(panel);
37497 var el = panel.getEl();
37498 if(el.dom.parentNode != this.mgr.el.dom){
37499 this.mgr.el.dom.appendChild(el.dom);
37501 if(panel.setRegion){
37502 panel.setRegion(this);
37504 this.panels.add(panel);
37505 el.setStyle("position", "absolute");
37506 if(!panel.background){
37507 this.setActivePanel(panel);
37508 if(this.config.initialSize && this.panels.getCount()==1){
37509 this.resizeTo(this.config.initialSize);
37512 this.fireEvent("paneladded", this, panel);
37517 * Returns true if the panel is in this region.
37518 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37519 * @return {Boolean}
37521 hasPanel : function(panel){
37522 if(typeof panel == "object"){ // must be panel obj
37523 panel = panel.getId();
37525 return this.getPanel(panel) ? true : false;
37529 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37530 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37531 * @param {Boolean} preservePanel Overrides the config preservePanel option
37532 * @return {Roo.ContentPanel} The panel that was removed
37534 remove : function(panel, preservePanel){
37535 panel = this.getPanel(panel);
37540 this.fireEvent("beforeremove", this, panel, e);
37541 if(e.cancel === true){
37544 var panelId = panel.getId();
37545 this.panels.removeKey(panelId);
37550 * Returns the panel specified or null if it's not in this region.
37551 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37552 * @return {Roo.ContentPanel}
37554 getPanel : function(id){
37555 if(typeof id == "object"){ // must be panel obj
37558 return this.panels.get(id);
37562 * Returns this regions position (north/south/east/west/center).
37565 getPosition: function(){
37566 return this.position;
37570 * Ext JS Library 1.1.1
37571 * Copyright(c) 2006-2007, Ext JS, LLC.
37573 * Originally Released Under LGPL - original licence link has changed is not relivant.
37576 * <script type="text/javascript">
37580 * @class Roo.bootstrap.layout.Region
37581 * @extends Roo.bootstrap.layout.Basic
37582 * This class represents a region in a layout manager.
37584 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37585 * @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})
37586 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37587 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37588 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37589 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37590 * @cfg {String} title The title for the region (overrides panel titles)
37591 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37592 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37593 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37594 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37595 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37596 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37597 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37598 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37599 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37600 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37602 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37603 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37604 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37605 * @cfg {Number} width For East/West panels
37606 * @cfg {Number} height For North/South panels
37607 * @cfg {Boolean} split To show the splitter
37608 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37610 * @cfg {string} cls Extra CSS classes to add to region
37612 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37613 * @cfg {string} region the region that it inhabits..
37616 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37617 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37619 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37620 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37621 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37623 Roo.bootstrap.layout.Region = function(config)
37625 this.applyConfig(config);
37627 var mgr = config.mgr;
37628 var pos = config.region;
37629 config.skipConfig = true;
37630 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37633 this.onRender(mgr.el);
37636 this.visible = true;
37637 this.collapsed = false;
37638 this.unrendered_panels = [];
37641 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37643 position: '', // set by wrapper (eg. north/south etc..)
37644 unrendered_panels : null, // unrendered panels.
37646 tabPosition : false,
37648 mgr: false, // points to 'Border'
37651 createBody : function(){
37652 /** This region's body element
37653 * @type Roo.Element */
37654 this.bodyEl = this.el.createChild({
37656 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37660 onRender: function(ctr, pos)
37662 var dh = Roo.DomHelper;
37663 /** This region's container element
37664 * @type Roo.Element */
37665 this.el = dh.append(ctr.dom, {
37667 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37669 /** This region's title element
37670 * @type Roo.Element */
37672 this.titleEl = dh.append(this.el.dom, {
37674 unselectable: "on",
37675 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37677 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37678 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37682 this.titleEl.enableDisplayMode();
37683 /** This region's title text element
37684 * @type HTMLElement */
37685 this.titleTextEl = this.titleEl.dom.firstChild;
37686 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37688 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37689 this.closeBtn.enableDisplayMode();
37690 this.closeBtn.on("click", this.closeClicked, this);
37691 this.closeBtn.hide();
37693 this.createBody(this.config);
37694 if(this.config.hideWhenEmpty){
37696 this.on("paneladded", this.validateVisibility, this);
37697 this.on("panelremoved", this.validateVisibility, this);
37699 if(this.autoScroll){
37700 this.bodyEl.setStyle("overflow", "auto");
37702 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37704 //if(c.titlebar !== false){
37705 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37706 this.titleEl.hide();
37708 this.titleEl.show();
37709 if(this.config.title){
37710 this.titleTextEl.innerHTML = this.config.title;
37714 if(this.config.collapsed){
37715 this.collapse(true);
37717 if(this.config.hidden){
37721 if (this.unrendered_panels && this.unrendered_panels.length) {
37722 for (var i =0;i< this.unrendered_panels.length; i++) {
37723 this.add(this.unrendered_panels[i]);
37725 this.unrendered_panels = null;
37731 applyConfig : function(c)
37734 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37735 var dh = Roo.DomHelper;
37736 if(c.titlebar !== false){
37737 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37738 this.collapseBtn.on("click", this.collapse, this);
37739 this.collapseBtn.enableDisplayMode();
37741 if(c.showPin === true || this.showPin){
37742 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37743 this.stickBtn.enableDisplayMode();
37744 this.stickBtn.on("click", this.expand, this);
37745 this.stickBtn.hide();
37750 /** This region's collapsed element
37751 * @type Roo.Element */
37754 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37755 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37758 if(c.floatable !== false){
37759 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37760 this.collapsedEl.on("click", this.collapseClick, this);
37763 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37764 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37765 id: "message", unselectable: "on", style:{"float":"left"}});
37766 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37768 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37769 this.expandBtn.on("click", this.expand, this);
37773 if(this.collapseBtn){
37774 this.collapseBtn.setVisible(c.collapsible == true);
37777 this.cmargins = c.cmargins || this.cmargins ||
37778 (this.position == "west" || this.position == "east" ?
37779 {top: 0, left: 2, right:2, bottom: 0} :
37780 {top: 2, left: 0, right:0, bottom: 2});
37782 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37785 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37787 this.autoScroll = c.autoScroll || false;
37792 this.duration = c.duration || .30;
37793 this.slideDuration = c.slideDuration || .45;
37798 * Returns true if this region is currently visible.
37799 * @return {Boolean}
37801 isVisible : function(){
37802 return this.visible;
37806 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37807 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
37809 //setCollapsedTitle : function(title){
37810 // title = title || " ";
37811 // if(this.collapsedTitleTextEl){
37812 // this.collapsedTitleTextEl.innerHTML = title;
37816 getBox : function(){
37818 // if(!this.collapsed){
37819 b = this.el.getBox(false, true);
37821 // b = this.collapsedEl.getBox(false, true);
37826 getMargins : function(){
37827 return this.margins;
37828 //return this.collapsed ? this.cmargins : this.margins;
37831 highlight : function(){
37832 this.el.addClass("x-layout-panel-dragover");
37835 unhighlight : function(){
37836 this.el.removeClass("x-layout-panel-dragover");
37839 updateBox : function(box)
37841 if (!this.bodyEl) {
37842 return; // not rendered yet..
37846 if(!this.collapsed){
37847 this.el.dom.style.left = box.x + "px";
37848 this.el.dom.style.top = box.y + "px";
37849 this.updateBody(box.width, box.height);
37851 this.collapsedEl.dom.style.left = box.x + "px";
37852 this.collapsedEl.dom.style.top = box.y + "px";
37853 this.collapsedEl.setSize(box.width, box.height);
37856 this.tabs.autoSizeTabs();
37860 updateBody : function(w, h)
37863 this.el.setWidth(w);
37864 w -= this.el.getBorderWidth("rl");
37865 if(this.config.adjustments){
37866 w += this.config.adjustments[0];
37869 if(h !== null && h > 0){
37870 this.el.setHeight(h);
37871 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37872 h -= this.el.getBorderWidth("tb");
37873 if(this.config.adjustments){
37874 h += this.config.adjustments[1];
37876 this.bodyEl.setHeight(h);
37878 h = this.tabs.syncHeight(h);
37881 if(this.panelSize){
37882 w = w !== null ? w : this.panelSize.width;
37883 h = h !== null ? h : this.panelSize.height;
37885 if(this.activePanel){
37886 var el = this.activePanel.getEl();
37887 w = w !== null ? w : el.getWidth();
37888 h = h !== null ? h : el.getHeight();
37889 this.panelSize = {width: w, height: h};
37890 this.activePanel.setSize(w, h);
37892 if(Roo.isIE && this.tabs){
37893 this.tabs.el.repaint();
37898 * Returns the container element for this region.
37899 * @return {Roo.Element}
37901 getEl : function(){
37906 * Hides this region.
37909 //if(!this.collapsed){
37910 this.el.dom.style.left = "-2000px";
37913 // this.collapsedEl.dom.style.left = "-2000px";
37914 // this.collapsedEl.hide();
37916 this.visible = false;
37917 this.fireEvent("visibilitychange", this, false);
37921 * Shows this region if it was previously hidden.
37924 //if(!this.collapsed){
37927 // this.collapsedEl.show();
37929 this.visible = true;
37930 this.fireEvent("visibilitychange", this, true);
37933 closeClicked : function(){
37934 if(this.activePanel){
37935 this.remove(this.activePanel);
37939 collapseClick : function(e){
37941 e.stopPropagation();
37944 e.stopPropagation();
37950 * Collapses this region.
37951 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37954 collapse : function(skipAnim, skipCheck = false){
37955 if(this.collapsed) {
37959 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37961 this.collapsed = true;
37963 this.split.el.hide();
37965 if(this.config.animate && skipAnim !== true){
37966 this.fireEvent("invalidated", this);
37967 this.animateCollapse();
37969 this.el.setLocation(-20000,-20000);
37971 this.collapsedEl.show();
37972 this.fireEvent("collapsed", this);
37973 this.fireEvent("invalidated", this);
37979 animateCollapse : function(){
37984 * Expands this region if it was previously collapsed.
37985 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37986 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37989 expand : function(e, skipAnim){
37991 e.stopPropagation();
37993 if(!this.collapsed || this.el.hasActiveFx()) {
37997 this.afterSlideIn();
38000 this.collapsed = false;
38001 if(this.config.animate && skipAnim !== true){
38002 this.animateExpand();
38006 this.split.el.show();
38008 this.collapsedEl.setLocation(-2000,-2000);
38009 this.collapsedEl.hide();
38010 this.fireEvent("invalidated", this);
38011 this.fireEvent("expanded", this);
38015 animateExpand : function(){
38019 initTabs : function()
38021 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38023 var ts = new Roo.bootstrap.panel.Tabs({
38024 el: this.bodyEl.dom,
38026 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38027 disableTooltips: this.config.disableTabTips,
38028 toolbar : this.config.toolbar
38031 if(this.config.hideTabs){
38032 ts.stripWrap.setDisplayed(false);
38035 ts.resizeTabs = this.config.resizeTabs === true;
38036 ts.minTabWidth = this.config.minTabWidth || 40;
38037 ts.maxTabWidth = this.config.maxTabWidth || 250;
38038 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38039 ts.monitorResize = false;
38040 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38041 ts.bodyEl.addClass('roo-layout-tabs-body');
38042 this.panels.each(this.initPanelAsTab, this);
38045 initPanelAsTab : function(panel){
38046 var ti = this.tabs.addTab(
38050 this.config.closeOnTab && panel.isClosable(),
38053 if(panel.tabTip !== undefined){
38054 ti.setTooltip(panel.tabTip);
38056 ti.on("activate", function(){
38057 this.setActivePanel(panel);
38060 if(this.config.closeOnTab){
38061 ti.on("beforeclose", function(t, e){
38063 this.remove(panel);
38067 panel.tabItem = ti;
38072 updatePanelTitle : function(panel, title)
38074 if(this.activePanel == panel){
38075 this.updateTitle(title);
38078 var ti = this.tabs.getTab(panel.getEl().id);
38080 if(panel.tabTip !== undefined){
38081 ti.setTooltip(panel.tabTip);
38086 updateTitle : function(title){
38087 if(this.titleTextEl && !this.config.title){
38088 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38092 setActivePanel : function(panel)
38094 panel = this.getPanel(panel);
38095 if(this.activePanel && this.activePanel != panel){
38096 if(this.activePanel.setActiveState(false) === false){
38100 this.activePanel = panel;
38101 panel.setActiveState(true);
38102 if(this.panelSize){
38103 panel.setSize(this.panelSize.width, this.panelSize.height);
38106 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38108 this.updateTitle(panel.getTitle());
38110 this.fireEvent("invalidated", this);
38112 this.fireEvent("panelactivated", this, panel);
38116 * Shows the specified panel.
38117 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38118 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38120 showPanel : function(panel)
38122 panel = this.getPanel(panel);
38125 var tab = this.tabs.getTab(panel.getEl().id);
38126 if(tab.isHidden()){
38127 this.tabs.unhideTab(tab.id);
38131 this.setActivePanel(panel);
38138 * Get the active panel for this region.
38139 * @return {Roo.ContentPanel} The active panel or null
38141 getActivePanel : function(){
38142 return this.activePanel;
38145 validateVisibility : function(){
38146 if(this.panels.getCount() < 1){
38147 this.updateTitle(" ");
38148 this.closeBtn.hide();
38151 if(!this.isVisible()){
38158 * Adds the passed ContentPanel(s) to this region.
38159 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38160 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38162 add : function(panel)
38164 if(arguments.length > 1){
38165 for(var i = 0, len = arguments.length; i < len; i++) {
38166 this.add(arguments[i]);
38171 // if we have not been rendered yet, then we can not really do much of this..
38172 if (!this.bodyEl) {
38173 this.unrendered_panels.push(panel);
38180 if(this.hasPanel(panel)){
38181 this.showPanel(panel);
38184 panel.setRegion(this);
38185 this.panels.add(panel);
38186 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38187 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38188 // and hide them... ???
38189 this.bodyEl.dom.appendChild(panel.getEl().dom);
38190 if(panel.background !== true){
38191 this.setActivePanel(panel);
38193 this.fireEvent("paneladded", this, panel);
38200 this.initPanelAsTab(panel);
38204 if(panel.background !== true){
38205 this.tabs.activate(panel.getEl().id);
38207 this.fireEvent("paneladded", this, panel);
38212 * Hides the tab for the specified panel.
38213 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38215 hidePanel : function(panel){
38216 if(this.tabs && (panel = this.getPanel(panel))){
38217 this.tabs.hideTab(panel.getEl().id);
38222 * Unhides the tab for a previously hidden panel.
38223 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38225 unhidePanel : function(panel){
38226 if(this.tabs && (panel = this.getPanel(panel))){
38227 this.tabs.unhideTab(panel.getEl().id);
38231 clearPanels : function(){
38232 while(this.panels.getCount() > 0){
38233 this.remove(this.panels.first());
38238 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38239 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38240 * @param {Boolean} preservePanel Overrides the config preservePanel option
38241 * @return {Roo.ContentPanel} The panel that was removed
38243 remove : function(panel, preservePanel)
38245 panel = this.getPanel(panel);
38250 this.fireEvent("beforeremove", this, panel, e);
38251 if(e.cancel === true){
38254 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38255 var panelId = panel.getId();
38256 this.panels.removeKey(panelId);
38258 document.body.appendChild(panel.getEl().dom);
38261 this.tabs.removeTab(panel.getEl().id);
38262 }else if (!preservePanel){
38263 this.bodyEl.dom.removeChild(panel.getEl().dom);
38265 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38266 var p = this.panels.first();
38267 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38268 tempEl.appendChild(p.getEl().dom);
38269 this.bodyEl.update("");
38270 this.bodyEl.dom.appendChild(p.getEl().dom);
38272 this.updateTitle(p.getTitle());
38274 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38275 this.setActivePanel(p);
38277 panel.setRegion(null);
38278 if(this.activePanel == panel){
38279 this.activePanel = null;
38281 if(this.config.autoDestroy !== false && preservePanel !== true){
38282 try{panel.destroy();}catch(e){}
38284 this.fireEvent("panelremoved", this, panel);
38289 * Returns the TabPanel component used by this region
38290 * @return {Roo.TabPanel}
38292 getTabs : function(){
38296 createTool : function(parentEl, className){
38297 var btn = Roo.DomHelper.append(parentEl, {
38299 cls: "x-layout-tools-button",
38302 cls: "roo-layout-tools-button-inner " + className,
38306 btn.addClassOnOver("roo-layout-tools-button-over");
38311 * Ext JS Library 1.1.1
38312 * Copyright(c) 2006-2007, Ext JS, LLC.
38314 * Originally Released Under LGPL - original licence link has changed is not relivant.
38317 * <script type="text/javascript">
38323 * @class Roo.SplitLayoutRegion
38324 * @extends Roo.LayoutRegion
38325 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38327 Roo.bootstrap.layout.Split = function(config){
38328 this.cursor = config.cursor;
38329 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38332 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38334 splitTip : "Drag to resize.",
38335 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38336 useSplitTips : false,
38338 applyConfig : function(config){
38339 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38342 onRender : function(ctr,pos) {
38344 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38345 if(!this.config.split){
38350 var splitEl = Roo.DomHelper.append(ctr.dom, {
38352 id: this.el.id + "-split",
38353 cls: "roo-layout-split roo-layout-split-"+this.position,
38356 /** The SplitBar for this region
38357 * @type Roo.SplitBar */
38358 // does not exist yet...
38359 Roo.log([this.position, this.orientation]);
38361 this.split = new Roo.bootstrap.SplitBar({
38362 dragElement : splitEl,
38363 resizingElement: this.el,
38364 orientation : this.orientation
38367 this.split.on("moved", this.onSplitMove, this);
38368 this.split.useShim = this.config.useShim === true;
38369 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38370 if(this.useSplitTips){
38371 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38373 //if(config.collapsible){
38374 // this.split.el.on("dblclick", this.collapse, this);
38377 if(typeof this.config.minSize != "undefined"){
38378 this.split.minSize = this.config.minSize;
38380 if(typeof this.config.maxSize != "undefined"){
38381 this.split.maxSize = this.config.maxSize;
38383 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38384 this.hideSplitter();
38389 getHMaxSize : function(){
38390 var cmax = this.config.maxSize || 10000;
38391 var center = this.mgr.getRegion("center");
38392 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38395 getVMaxSize : function(){
38396 var cmax = this.config.maxSize || 10000;
38397 var center = this.mgr.getRegion("center");
38398 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38401 onSplitMove : function(split, newSize){
38402 this.fireEvent("resized", this, newSize);
38406 * Returns the {@link Roo.SplitBar} for this region.
38407 * @return {Roo.SplitBar}
38409 getSplitBar : function(){
38414 this.hideSplitter();
38415 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38418 hideSplitter : function(){
38420 this.split.el.setLocation(-2000,-2000);
38421 this.split.el.hide();
38427 this.split.el.show();
38429 Roo.bootstrap.layout.Split.superclass.show.call(this);
38432 beforeSlide: function(){
38433 if(Roo.isGecko){// firefox overflow auto bug workaround
38434 this.bodyEl.clip();
38436 this.tabs.bodyEl.clip();
38438 if(this.activePanel){
38439 this.activePanel.getEl().clip();
38441 if(this.activePanel.beforeSlide){
38442 this.activePanel.beforeSlide();
38448 afterSlide : function(){
38449 if(Roo.isGecko){// firefox overflow auto bug workaround
38450 this.bodyEl.unclip();
38452 this.tabs.bodyEl.unclip();
38454 if(this.activePanel){
38455 this.activePanel.getEl().unclip();
38456 if(this.activePanel.afterSlide){
38457 this.activePanel.afterSlide();
38463 initAutoHide : function(){
38464 if(this.autoHide !== false){
38465 if(!this.autoHideHd){
38466 var st = new Roo.util.DelayedTask(this.slideIn, this);
38467 this.autoHideHd = {
38468 "mouseout": function(e){
38469 if(!e.within(this.el, true)){
38473 "mouseover" : function(e){
38479 this.el.on(this.autoHideHd);
38483 clearAutoHide : function(){
38484 if(this.autoHide !== false){
38485 this.el.un("mouseout", this.autoHideHd.mouseout);
38486 this.el.un("mouseover", this.autoHideHd.mouseover);
38490 clearMonitor : function(){
38491 Roo.get(document).un("click", this.slideInIf, this);
38494 // these names are backwards but not changed for compat
38495 slideOut : function(){
38496 if(this.isSlid || this.el.hasActiveFx()){
38499 this.isSlid = true;
38500 if(this.collapseBtn){
38501 this.collapseBtn.hide();
38503 this.closeBtnState = this.closeBtn.getStyle('display');
38504 this.closeBtn.hide();
38506 this.stickBtn.show();
38509 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38510 this.beforeSlide();
38511 this.el.setStyle("z-index", 10001);
38512 this.el.slideIn(this.getSlideAnchor(), {
38513 callback: function(){
38515 this.initAutoHide();
38516 Roo.get(document).on("click", this.slideInIf, this);
38517 this.fireEvent("slideshow", this);
38524 afterSlideIn : function(){
38525 this.clearAutoHide();
38526 this.isSlid = false;
38527 this.clearMonitor();
38528 this.el.setStyle("z-index", "");
38529 if(this.collapseBtn){
38530 this.collapseBtn.show();
38532 this.closeBtn.setStyle('display', this.closeBtnState);
38534 this.stickBtn.hide();
38536 this.fireEvent("slidehide", this);
38539 slideIn : function(cb){
38540 if(!this.isSlid || this.el.hasActiveFx()){
38544 this.isSlid = false;
38545 this.beforeSlide();
38546 this.el.slideOut(this.getSlideAnchor(), {
38547 callback: function(){
38548 this.el.setLeftTop(-10000, -10000);
38550 this.afterSlideIn();
38558 slideInIf : function(e){
38559 if(!e.within(this.el)){
38564 animateCollapse : function(){
38565 this.beforeSlide();
38566 this.el.setStyle("z-index", 20000);
38567 var anchor = this.getSlideAnchor();
38568 this.el.slideOut(anchor, {
38569 callback : function(){
38570 this.el.setStyle("z-index", "");
38571 this.collapsedEl.slideIn(anchor, {duration:.3});
38573 this.el.setLocation(-10000,-10000);
38575 this.fireEvent("collapsed", this);
38582 animateExpand : function(){
38583 this.beforeSlide();
38584 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38585 this.el.setStyle("z-index", 20000);
38586 this.collapsedEl.hide({
38589 this.el.slideIn(this.getSlideAnchor(), {
38590 callback : function(){
38591 this.el.setStyle("z-index", "");
38594 this.split.el.show();
38596 this.fireEvent("invalidated", this);
38597 this.fireEvent("expanded", this);
38625 getAnchor : function(){
38626 return this.anchors[this.position];
38629 getCollapseAnchor : function(){
38630 return this.canchors[this.position];
38633 getSlideAnchor : function(){
38634 return this.sanchors[this.position];
38637 getAlignAdj : function(){
38638 var cm = this.cmargins;
38639 switch(this.position){
38655 getExpandAdj : function(){
38656 var c = this.collapsedEl, cm = this.cmargins;
38657 switch(this.position){
38659 return [-(cm.right+c.getWidth()+cm.left), 0];
38662 return [cm.right+c.getWidth()+cm.left, 0];
38665 return [0, -(cm.top+cm.bottom+c.getHeight())];
38668 return [0, cm.top+cm.bottom+c.getHeight()];
38674 * Ext JS Library 1.1.1
38675 * Copyright(c) 2006-2007, Ext JS, LLC.
38677 * Originally Released Under LGPL - original licence link has changed is not relivant.
38680 * <script type="text/javascript">
38683 * These classes are private internal classes
38685 Roo.bootstrap.layout.Center = function(config){
38686 config.region = "center";
38687 Roo.bootstrap.layout.Region.call(this, config);
38688 this.visible = true;
38689 this.minWidth = config.minWidth || 20;
38690 this.minHeight = config.minHeight || 20;
38693 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38695 // center panel can't be hidden
38699 // center panel can't be hidden
38702 getMinWidth: function(){
38703 return this.minWidth;
38706 getMinHeight: function(){
38707 return this.minHeight;
38721 Roo.bootstrap.layout.North = function(config)
38723 config.region = 'north';
38724 config.cursor = 'n-resize';
38726 Roo.bootstrap.layout.Split.call(this, config);
38730 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38731 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38732 this.split.el.addClass("roo-layout-split-v");
38734 var size = config.initialSize || config.height;
38735 if(typeof size != "undefined"){
38736 this.el.setHeight(size);
38739 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38741 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38745 getBox : function(){
38746 if(this.collapsed){
38747 return this.collapsedEl.getBox();
38749 var box = this.el.getBox();
38751 box.height += this.split.el.getHeight();
38756 updateBox : function(box){
38757 if(this.split && !this.collapsed){
38758 box.height -= this.split.el.getHeight();
38759 this.split.el.setLeft(box.x);
38760 this.split.el.setTop(box.y+box.height);
38761 this.split.el.setWidth(box.width);
38763 if(this.collapsed){
38764 this.updateBody(box.width, null);
38766 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38774 Roo.bootstrap.layout.South = function(config){
38775 config.region = 'south';
38776 config.cursor = 's-resize';
38777 Roo.bootstrap.layout.Split.call(this, config);
38779 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38780 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38781 this.split.el.addClass("roo-layout-split-v");
38783 var size = config.initialSize || config.height;
38784 if(typeof size != "undefined"){
38785 this.el.setHeight(size);
38789 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38790 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38791 getBox : function(){
38792 if(this.collapsed){
38793 return this.collapsedEl.getBox();
38795 var box = this.el.getBox();
38797 var sh = this.split.el.getHeight();
38804 updateBox : function(box){
38805 if(this.split && !this.collapsed){
38806 var sh = this.split.el.getHeight();
38809 this.split.el.setLeft(box.x);
38810 this.split.el.setTop(box.y-sh);
38811 this.split.el.setWidth(box.width);
38813 if(this.collapsed){
38814 this.updateBody(box.width, null);
38816 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38820 Roo.bootstrap.layout.East = function(config){
38821 config.region = "east";
38822 config.cursor = "e-resize";
38823 Roo.bootstrap.layout.Split.call(this, config);
38825 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38826 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38827 this.split.el.addClass("roo-layout-split-h");
38829 var size = config.initialSize || config.width;
38830 if(typeof size != "undefined"){
38831 this.el.setWidth(size);
38834 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38835 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38836 getBox : function(){
38837 if(this.collapsed){
38838 return this.collapsedEl.getBox();
38840 var box = this.el.getBox();
38842 var sw = this.split.el.getWidth();
38849 updateBox : function(box){
38850 if(this.split && !this.collapsed){
38851 var sw = this.split.el.getWidth();
38853 this.split.el.setLeft(box.x);
38854 this.split.el.setTop(box.y);
38855 this.split.el.setHeight(box.height);
38858 if(this.collapsed){
38859 this.updateBody(null, box.height);
38861 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38865 Roo.bootstrap.layout.West = function(config){
38866 config.region = "west";
38867 config.cursor = "w-resize";
38869 Roo.bootstrap.layout.Split.call(this, config);
38871 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38872 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38873 this.split.el.addClass("roo-layout-split-h");
38877 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38878 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38880 onRender: function(ctr, pos)
38882 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38883 var size = this.config.initialSize || this.config.width;
38884 if(typeof size != "undefined"){
38885 this.el.setWidth(size);
38889 getBox : function(){
38890 if(this.collapsed){
38891 return this.collapsedEl.getBox();
38893 var box = this.el.getBox();
38895 box.width += this.split.el.getWidth();
38900 updateBox : function(box){
38901 if(this.split && !this.collapsed){
38902 var sw = this.split.el.getWidth();
38904 this.split.el.setLeft(box.x+box.width);
38905 this.split.el.setTop(box.y);
38906 this.split.el.setHeight(box.height);
38908 if(this.collapsed){
38909 this.updateBody(null, box.height);
38911 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38913 });Roo.namespace("Roo.bootstrap.panel");/*
38915 * Ext JS Library 1.1.1
38916 * Copyright(c) 2006-2007, Ext JS, LLC.
38918 * Originally Released Under LGPL - original licence link has changed is not relivant.
38921 * <script type="text/javascript">
38924 * @class Roo.ContentPanel
38925 * @extends Roo.util.Observable
38926 * A basic ContentPanel element.
38927 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
38928 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
38929 * @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
38930 * @cfg {Boolean} closable True if the panel can be closed/removed
38931 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
38932 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38933 * @cfg {Toolbar} toolbar A toolbar for this panel
38934 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
38935 * @cfg {String} title The title for this panel
38936 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38937 * @cfg {String} url Calls {@link #setUrl} with this value
38938 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38939 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
38940 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
38941 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
38942 * @cfg {Boolean} badges render the badges
38945 * Create a new ContentPanel.
38946 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38947 * @param {String/Object} config A string to set only the title or a config object
38948 * @param {String} content (optional) Set the HTML content for this panel
38949 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38951 Roo.bootstrap.panel.Content = function( config){
38953 this.tpl = config.tpl || false;
38955 var el = config.el;
38956 var content = config.content;
38958 if(config.autoCreate){ // xtype is available if this is called from factory
38961 this.el = Roo.get(el);
38962 if(!this.el && config && config.autoCreate){
38963 if(typeof config.autoCreate == "object"){
38964 if(!config.autoCreate.id){
38965 config.autoCreate.id = config.id||el;
38967 this.el = Roo.DomHelper.append(document.body,
38968 config.autoCreate, true);
38970 var elcfg = { tag: "div",
38971 cls: "roo-layout-inactive-content",
38975 elcfg.html = config.html;
38979 this.el = Roo.DomHelper.append(document.body, elcfg , true);
38982 this.closable = false;
38983 this.loaded = false;
38984 this.active = false;
38987 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38989 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38991 this.wrapEl = this.el; //this.el.wrap();
38993 if (config.toolbar.items) {
38994 ti = config.toolbar.items ;
38995 delete config.toolbar.items ;
38999 this.toolbar.render(this.wrapEl, 'before');
39000 for(var i =0;i < ti.length;i++) {
39001 // Roo.log(['add child', items[i]]);
39002 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39004 this.toolbar.items = nitems;
39005 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39006 delete config.toolbar;
39010 // xtype created footer. - not sure if will work as we normally have to render first..
39011 if (this.footer && !this.footer.el && this.footer.xtype) {
39012 if (!this.wrapEl) {
39013 this.wrapEl = this.el.wrap();
39016 this.footer.container = this.wrapEl.createChild();
39018 this.footer = Roo.factory(this.footer, Roo);
39023 if(typeof config == "string"){
39024 this.title = config;
39026 Roo.apply(this, config);
39030 this.resizeEl = Roo.get(this.resizeEl, true);
39032 this.resizeEl = this.el;
39034 // handle view.xtype
39042 * Fires when this panel is activated.
39043 * @param {Roo.ContentPanel} this
39047 * @event deactivate
39048 * Fires when this panel is activated.
39049 * @param {Roo.ContentPanel} this
39051 "deactivate" : true,
39055 * Fires when this panel is resized if fitToFrame is true.
39056 * @param {Roo.ContentPanel} this
39057 * @param {Number} width The width after any component adjustments
39058 * @param {Number} height The height after any component adjustments
39064 * Fires when this tab is created
39065 * @param {Roo.ContentPanel} this
39076 if(this.autoScroll){
39077 this.resizeEl.setStyle("overflow", "auto");
39079 // fix randome scrolling
39080 //this.el.on('scroll', function() {
39081 // Roo.log('fix random scolling');
39082 // this.scrollTo('top',0);
39085 content = content || this.content;
39087 this.setContent(content);
39089 if(config && config.url){
39090 this.setUrl(this.url, this.params, this.loadOnce);
39095 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39097 if (this.view && typeof(this.view.xtype) != 'undefined') {
39098 this.view.el = this.el.appendChild(document.createElement("div"));
39099 this.view = Roo.factory(this.view);
39100 this.view.render && this.view.render(false, '');
39104 this.fireEvent('render', this);
39107 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39111 setRegion : function(region){
39112 this.region = region;
39113 this.setActiveClass(region && !this.background);
39117 setActiveClass: function(state)
39120 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39121 this.el.setStyle('position','relative');
39123 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39124 this.el.setStyle('position', 'absolute');
39129 * Returns the toolbar for this Panel if one was configured.
39130 * @return {Roo.Toolbar}
39132 getToolbar : function(){
39133 return this.toolbar;
39136 setActiveState : function(active)
39138 this.active = active;
39139 this.setActiveClass(active);
39141 if(this.fireEvent("deactivate", this) === false){
39146 this.fireEvent("activate", this);
39150 * Updates this panel's element
39151 * @param {String} content The new content
39152 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39154 setContent : function(content, loadScripts){
39155 this.el.update(content, loadScripts);
39158 ignoreResize : function(w, h){
39159 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39162 this.lastSize = {width: w, height: h};
39167 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39168 * @return {Roo.UpdateManager} The UpdateManager
39170 getUpdateManager : function(){
39171 return this.el.getUpdateManager();
39174 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39175 * @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:
39178 url: "your-url.php",
39179 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39180 callback: yourFunction,
39181 scope: yourObject, //(optional scope)
39184 text: "Loading...",
39189 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39190 * 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.
39191 * @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}
39192 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39193 * @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.
39194 * @return {Roo.ContentPanel} this
39197 var um = this.el.getUpdateManager();
39198 um.update.apply(um, arguments);
39204 * 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.
39205 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39206 * @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)
39207 * @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)
39208 * @return {Roo.UpdateManager} The UpdateManager
39210 setUrl : function(url, params, loadOnce){
39211 if(this.refreshDelegate){
39212 this.removeListener("activate", this.refreshDelegate);
39214 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39215 this.on("activate", this.refreshDelegate);
39216 return this.el.getUpdateManager();
39219 _handleRefresh : function(url, params, loadOnce){
39220 if(!loadOnce || !this.loaded){
39221 var updater = this.el.getUpdateManager();
39222 updater.update(url, params, this._setLoaded.createDelegate(this));
39226 _setLoaded : function(){
39227 this.loaded = true;
39231 * Returns this panel's id
39234 getId : function(){
39239 * Returns this panel's element - used by regiosn to add.
39240 * @return {Roo.Element}
39242 getEl : function(){
39243 return this.wrapEl || this.el;
39248 adjustForComponents : function(width, height)
39250 //Roo.log('adjustForComponents ');
39251 if(this.resizeEl != this.el){
39252 width -= this.el.getFrameWidth('lr');
39253 height -= this.el.getFrameWidth('tb');
39256 var te = this.toolbar.getEl();
39257 te.setWidth(width);
39258 height -= te.getHeight();
39261 var te = this.footer.getEl();
39262 te.setWidth(width);
39263 height -= te.getHeight();
39267 if(this.adjustments){
39268 width += this.adjustments[0];
39269 height += this.adjustments[1];
39271 return {"width": width, "height": height};
39274 setSize : function(width, height){
39275 if(this.fitToFrame && !this.ignoreResize(width, height)){
39276 if(this.fitContainer && this.resizeEl != this.el){
39277 this.el.setSize(width, height);
39279 var size = this.adjustForComponents(width, height);
39280 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39281 this.fireEvent('resize', this, size.width, size.height);
39286 * Returns this panel's title
39289 getTitle : function(){
39291 if (typeof(this.title) != 'object') {
39296 for (var k in this.title) {
39297 if (!this.title.hasOwnProperty(k)) {
39301 if (k.indexOf('-') >= 0) {
39302 var s = k.split('-');
39303 for (var i = 0; i<s.length; i++) {
39304 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39307 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39314 * Set this panel's title
39315 * @param {String} title
39317 setTitle : function(title){
39318 this.title = title;
39320 this.region.updatePanelTitle(this, title);
39325 * Returns true is this panel was configured to be closable
39326 * @return {Boolean}
39328 isClosable : function(){
39329 return this.closable;
39332 beforeSlide : function(){
39334 this.resizeEl.clip();
39337 afterSlide : function(){
39339 this.resizeEl.unclip();
39343 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39344 * Will fail silently if the {@link #setUrl} method has not been called.
39345 * This does not activate the panel, just updates its content.
39347 refresh : function(){
39348 if(this.refreshDelegate){
39349 this.loaded = false;
39350 this.refreshDelegate();
39355 * Destroys this panel
39357 destroy : function(){
39358 this.el.removeAllListeners();
39359 var tempEl = document.createElement("span");
39360 tempEl.appendChild(this.el.dom);
39361 tempEl.innerHTML = "";
39367 * form - if the content panel contains a form - this is a reference to it.
39368 * @type {Roo.form.Form}
39372 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39373 * This contains a reference to it.
39379 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39389 * @param {Object} cfg Xtype definition of item to add.
39393 getChildContainer: function () {
39394 return this.getEl();
39399 var ret = new Roo.factory(cfg);
39404 if (cfg.xtype.match(/^Form$/)) {
39407 //if (this.footer) {
39408 // el = this.footer.container.insertSibling(false, 'before');
39410 el = this.el.createChild();
39413 this.form = new Roo.form.Form(cfg);
39416 if ( this.form.allItems.length) {
39417 this.form.render(el.dom);
39421 // should only have one of theses..
39422 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39423 // views.. should not be just added - used named prop 'view''
39425 cfg.el = this.el.appendChild(document.createElement("div"));
39428 var ret = new Roo.factory(cfg);
39430 ret.render && ret.render(false, ''); // render blank..
39440 * @class Roo.bootstrap.panel.Grid
39441 * @extends Roo.bootstrap.panel.Content
39443 * Create a new GridPanel.
39444 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39445 * @param {Object} config A the config object
39451 Roo.bootstrap.panel.Grid = function(config)
39455 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39456 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39458 config.el = this.wrapper;
39459 //this.el = this.wrapper;
39461 if (config.container) {
39462 // ctor'ed from a Border/panel.grid
39465 this.wrapper.setStyle("overflow", "hidden");
39466 this.wrapper.addClass('roo-grid-container');
39471 if(config.toolbar){
39472 var tool_el = this.wrapper.createChild();
39473 this.toolbar = Roo.factory(config.toolbar);
39475 if (config.toolbar.items) {
39476 ti = config.toolbar.items ;
39477 delete config.toolbar.items ;
39481 this.toolbar.render(tool_el);
39482 for(var i =0;i < ti.length;i++) {
39483 // Roo.log(['add child', items[i]]);
39484 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39486 this.toolbar.items = nitems;
39488 delete config.toolbar;
39491 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39492 config.grid.scrollBody = true;;
39493 config.grid.monitorWindowResize = false; // turn off autosizing
39494 config.grid.autoHeight = false;
39495 config.grid.autoWidth = false;
39497 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39499 if (config.background) {
39500 // render grid on panel activation (if panel background)
39501 this.on('activate', function(gp) {
39502 if (!gp.grid.rendered) {
39503 gp.grid.render(this.wrapper);
39504 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39509 this.grid.render(this.wrapper);
39510 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39513 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39514 // ??? needed ??? config.el = this.wrapper;
39519 // xtype created footer. - not sure if will work as we normally have to render first..
39520 if (this.footer && !this.footer.el && this.footer.xtype) {
39522 var ctr = this.grid.getView().getFooterPanel(true);
39523 this.footer.dataSource = this.grid.dataSource;
39524 this.footer = Roo.factory(this.footer, Roo);
39525 this.footer.render(ctr);
39535 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39536 getId : function(){
39537 return this.grid.id;
39541 * Returns the grid for this panel
39542 * @return {Roo.bootstrap.Table}
39544 getGrid : function(){
39548 setSize : function(width, height){
39549 if(!this.ignoreResize(width, height)){
39550 var grid = this.grid;
39551 var size = this.adjustForComponents(width, height);
39552 var gridel = grid.getGridEl();
39553 gridel.setSize(size.width, size.height);
39555 var thd = grid.getGridEl().select('thead',true).first();
39556 var tbd = grid.getGridEl().select('tbody', true).first();
39558 tbd.setSize(width, height - thd.getHeight());
39567 beforeSlide : function(){
39568 this.grid.getView().scroller.clip();
39571 afterSlide : function(){
39572 this.grid.getView().scroller.unclip();
39575 destroy : function(){
39576 this.grid.destroy();
39578 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39583 * @class Roo.bootstrap.panel.Nest
39584 * @extends Roo.bootstrap.panel.Content
39586 * Create a new Panel, that can contain a layout.Border.
39589 * @param {Roo.BorderLayout} layout The layout for this panel
39590 * @param {String/Object} config A string to set only the title or a config object
39592 Roo.bootstrap.panel.Nest = function(config)
39594 // construct with only one argument..
39595 /* FIXME - implement nicer consturctors
39596 if (layout.layout) {
39598 layout = config.layout;
39599 delete config.layout;
39601 if (layout.xtype && !layout.getEl) {
39602 // then layout needs constructing..
39603 layout = Roo.factory(layout, Roo);
39607 config.el = config.layout.getEl();
39609 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39611 config.layout.monitorWindowResize = false; // turn off autosizing
39612 this.layout = config.layout;
39613 this.layout.getEl().addClass("roo-layout-nested-layout");
39614 this.layout.parent = this;
39621 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39623 setSize : function(width, height){
39624 if(!this.ignoreResize(width, height)){
39625 var size = this.adjustForComponents(width, height);
39626 var el = this.layout.getEl();
39627 if (size.height < 1) {
39628 el.setWidth(size.width);
39630 el.setSize(size.width, size.height);
39632 var touch = el.dom.offsetWidth;
39633 this.layout.layout();
39634 // ie requires a double layout on the first pass
39635 if(Roo.isIE && !this.initialized){
39636 this.initialized = true;
39637 this.layout.layout();
39642 // activate all subpanels if not currently active..
39644 setActiveState : function(active){
39645 this.active = active;
39646 this.setActiveClass(active);
39649 this.fireEvent("deactivate", this);
39653 this.fireEvent("activate", this);
39654 // not sure if this should happen before or after..
39655 if (!this.layout) {
39656 return; // should not happen..
39659 for (var r in this.layout.regions) {
39660 reg = this.layout.getRegion(r);
39661 if (reg.getActivePanel()) {
39662 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39663 reg.setActivePanel(reg.getActivePanel());
39666 if (!reg.panels.length) {
39669 reg.showPanel(reg.getPanel(0));
39678 * Returns the nested BorderLayout for this panel
39679 * @return {Roo.BorderLayout}
39681 getLayout : function(){
39682 return this.layout;
39686 * Adds a xtype elements to the layout of the nested panel
39690 xtype : 'ContentPanel',
39697 xtype : 'NestedLayoutPanel',
39703 items : [ ... list of content panels or nested layout panels.. ]
39707 * @param {Object} cfg Xtype definition of item to add.
39709 addxtype : function(cfg) {
39710 return this.layout.addxtype(cfg);
39715 * Ext JS Library 1.1.1
39716 * Copyright(c) 2006-2007, Ext JS, LLC.
39718 * Originally Released Under LGPL - original licence link has changed is not relivant.
39721 * <script type="text/javascript">
39724 * @class Roo.TabPanel
39725 * @extends Roo.util.Observable
39726 * A lightweight tab container.
39730 // basic tabs 1, built from existing content
39731 var tabs = new Roo.TabPanel("tabs1");
39732 tabs.addTab("script", "View Script");
39733 tabs.addTab("markup", "View Markup");
39734 tabs.activate("script");
39736 // more advanced tabs, built from javascript
39737 var jtabs = new Roo.TabPanel("jtabs");
39738 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39740 // set up the UpdateManager
39741 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39742 var updater = tab2.getUpdateManager();
39743 updater.setDefaultUrl("ajax1.htm");
39744 tab2.on('activate', updater.refresh, updater, true);
39746 // Use setUrl for Ajax loading
39747 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39748 tab3.setUrl("ajax2.htm", null, true);
39751 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39754 jtabs.activate("jtabs-1");
39757 * Create a new TabPanel.
39758 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39759 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39761 Roo.bootstrap.panel.Tabs = function(config){
39763 * The container element for this TabPanel.
39764 * @type Roo.Element
39766 this.el = Roo.get(config.el);
39769 if(typeof config == "boolean"){
39770 this.tabPosition = config ? "bottom" : "top";
39772 Roo.apply(this, config);
39776 if(this.tabPosition == "bottom"){
39777 // if tabs are at the bottom = create the body first.
39778 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39779 this.el.addClass("roo-tabs-bottom");
39781 // next create the tabs holders
39783 if (this.tabPosition == "west"){
39785 var reg = this.region; // fake it..
39787 if (!reg.mgr.parent) {
39790 reg = reg.mgr.parent.region;
39792 Roo.log("got nest?");
39794 if (reg.mgr.getRegion('west')) {
39795 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39796 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39797 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39798 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39799 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39807 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39808 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39809 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39810 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39815 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39818 // finally - if tabs are at the top, then create the body last..
39819 if(this.tabPosition != "bottom"){
39820 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39821 * @type Roo.Element
39823 this.bodyEl = Roo.get(this.createBody(this.el.dom));
39824 this.el.addClass("roo-tabs-top");
39828 this.bodyEl.setStyle("position", "relative");
39830 this.active = null;
39831 this.activateDelegate = this.activate.createDelegate(this);
39836 * Fires when the active tab changes
39837 * @param {Roo.TabPanel} this
39838 * @param {Roo.TabPanelItem} activePanel The new active tab
39842 * @event beforetabchange
39843 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39844 * @param {Roo.TabPanel} this
39845 * @param {Object} e Set cancel to true on this object to cancel the tab change
39846 * @param {Roo.TabPanelItem} tab The tab being changed to
39848 "beforetabchange" : true
39851 Roo.EventManager.onWindowResize(this.onResize, this);
39852 this.cpad = this.el.getPadding("lr");
39853 this.hiddenCount = 0;
39856 // toolbar on the tabbar support...
39857 if (this.toolbar) {
39858 alert("no toolbar support yet");
39859 this.toolbar = false;
39861 var tcfg = this.toolbar;
39862 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
39863 this.toolbar = new Roo.Toolbar(tcfg);
39864 if (Roo.isSafari) {
39865 var tbl = tcfg.container.child('table', true);
39866 tbl.setAttribute('width', '100%');
39874 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39877 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39879 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39881 tabPosition : "top",
39883 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39885 currentTabWidth : 0,
39887 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39891 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39895 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39897 preferredTabWidth : 175,
39899 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39901 resizeTabs : false,
39903 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39905 monitorResize : true,
39907 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
39909 toolbar : false, // set by caller..
39911 region : false, /// set by caller
39913 disableTooltips : true, // not used yet...
39916 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39917 * @param {String} id The id of the div to use <b>or create</b>
39918 * @param {String} text The text for the tab
39919 * @param {String} content (optional) Content to put in the TabPanelItem body
39920 * @param {Boolean} closable (optional) True to create a close icon on the tab
39921 * @return {Roo.TabPanelItem} The created TabPanelItem
39923 addTab : function(id, text, content, closable, tpl)
39925 var item = new Roo.bootstrap.panel.TabItem({
39929 closable : closable,
39932 this.addTabItem(item);
39934 item.setContent(content);
39940 * Returns the {@link Roo.TabPanelItem} with the specified id/index
39941 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39942 * @return {Roo.TabPanelItem}
39944 getTab : function(id){
39945 return this.items[id];
39949 * Hides the {@link Roo.TabPanelItem} with the specified id/index
39950 * @param {String/Number} id The id or index of the TabPanelItem to hide.
39952 hideTab : function(id){
39953 var t = this.items[id];
39956 this.hiddenCount++;
39957 this.autoSizeTabs();
39962 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39963 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39965 unhideTab : function(id){
39966 var t = this.items[id];
39968 t.setHidden(false);
39969 this.hiddenCount--;
39970 this.autoSizeTabs();
39975 * Adds an existing {@link Roo.TabPanelItem}.
39976 * @param {Roo.TabPanelItem} item The TabPanelItem to add
39978 addTabItem : function(item)
39980 this.items[item.id] = item;
39981 this.items.push(item);
39982 this.autoSizeTabs();
39983 // if(this.resizeTabs){
39984 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39985 // this.autoSizeTabs();
39987 // item.autoSize();
39992 * Removes a {@link Roo.TabPanelItem}.
39993 * @param {String/Number} id The id or index of the TabPanelItem to remove.
39995 removeTab : function(id){
39996 var items = this.items;
39997 var tab = items[id];
39998 if(!tab) { return; }
39999 var index = items.indexOf(tab);
40000 if(this.active == tab && items.length > 1){
40001 var newTab = this.getNextAvailable(index);
40006 this.stripEl.dom.removeChild(tab.pnode.dom);
40007 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40008 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40010 items.splice(index, 1);
40011 delete this.items[tab.id];
40012 tab.fireEvent("close", tab);
40013 tab.purgeListeners();
40014 this.autoSizeTabs();
40017 getNextAvailable : function(start){
40018 var items = this.items;
40020 // look for a next tab that will slide over to
40021 // replace the one being removed
40022 while(index < items.length){
40023 var item = items[++index];
40024 if(item && !item.isHidden()){
40028 // if one isn't found select the previous tab (on the left)
40031 var item = items[--index];
40032 if(item && !item.isHidden()){
40040 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40041 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40043 disableTab : function(id){
40044 var tab = this.items[id];
40045 if(tab && this.active != tab){
40051 * Enables a {@link Roo.TabPanelItem} that is disabled.
40052 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40054 enableTab : function(id){
40055 var tab = this.items[id];
40060 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40061 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40062 * @return {Roo.TabPanelItem} The TabPanelItem.
40064 activate : function(id)
40066 //Roo.log('activite:' + id);
40068 var tab = this.items[id];
40072 if(tab == this.active || tab.disabled){
40076 this.fireEvent("beforetabchange", this, e, tab);
40077 if(e.cancel !== true && !tab.disabled){
40079 this.active.hide();
40081 this.active = this.items[id];
40082 this.active.show();
40083 this.fireEvent("tabchange", this, this.active);
40089 * Gets the active {@link Roo.TabPanelItem}.
40090 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40092 getActiveTab : function(){
40093 return this.active;
40097 * Updates the tab body element to fit the height of the container element
40098 * for overflow scrolling
40099 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40101 syncHeight : function(targetHeight){
40102 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40103 var bm = this.bodyEl.getMargins();
40104 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40105 this.bodyEl.setHeight(newHeight);
40109 onResize : function(){
40110 if(this.monitorResize){
40111 this.autoSizeTabs();
40116 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40118 beginUpdate : function(){
40119 this.updating = true;
40123 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40125 endUpdate : function(){
40126 this.updating = false;
40127 this.autoSizeTabs();
40131 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40133 autoSizeTabs : function()
40135 var count = this.items.length;
40136 var vcount = count - this.hiddenCount;
40139 this.stripEl.hide();
40141 this.stripEl.show();
40144 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40149 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40150 var availWidth = Math.floor(w / vcount);
40151 var b = this.stripBody;
40152 if(b.getWidth() > w){
40153 var tabs = this.items;
40154 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40155 if(availWidth < this.minTabWidth){
40156 /*if(!this.sleft){ // incomplete scrolling code
40157 this.createScrollButtons();
40160 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40163 if(this.currentTabWidth < this.preferredTabWidth){
40164 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40170 * Returns the number of tabs in this TabPanel.
40173 getCount : function(){
40174 return this.items.length;
40178 * Resizes all the tabs to the passed width
40179 * @param {Number} The new width
40181 setTabWidth : function(width){
40182 this.currentTabWidth = width;
40183 for(var i = 0, len = this.items.length; i < len; i++) {
40184 if(!this.items[i].isHidden()) {
40185 this.items[i].setWidth(width);
40191 * Destroys this TabPanel
40192 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40194 destroy : function(removeEl){
40195 Roo.EventManager.removeResizeListener(this.onResize, this);
40196 for(var i = 0, len = this.items.length; i < len; i++){
40197 this.items[i].purgeListeners();
40199 if(removeEl === true){
40200 this.el.update("");
40205 createStrip : function(container)
40207 var strip = document.createElement("nav");
40208 strip.className = Roo.bootstrap.version == 4 ?
40209 "navbar-light bg-light" :
40210 "navbar navbar-default"; //"x-tabs-wrap";
40211 container.appendChild(strip);
40215 createStripList : function(strip)
40217 // div wrapper for retard IE
40218 // returns the "tr" element.
40219 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40220 //'<div class="x-tabs-strip-wrap">'+
40221 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40222 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40223 return strip.firstChild; //.firstChild.firstChild.firstChild;
40225 createBody : function(container)
40227 var body = document.createElement("div");
40228 Roo.id(body, "tab-body");
40229 //Roo.fly(body).addClass("x-tabs-body");
40230 Roo.fly(body).addClass("tab-content");
40231 container.appendChild(body);
40234 createItemBody :function(bodyEl, id){
40235 var body = Roo.getDom(id);
40237 body = document.createElement("div");
40240 //Roo.fly(body).addClass("x-tabs-item-body");
40241 Roo.fly(body).addClass("tab-pane");
40242 bodyEl.insertBefore(body, bodyEl.firstChild);
40246 createStripElements : function(stripEl, text, closable, tpl)
40248 var td = document.createElement("li"); // was td..
40249 td.className = 'nav-item';
40251 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40254 stripEl.appendChild(td);
40256 td.className = "x-tabs-closable";
40257 if(!this.closeTpl){
40258 this.closeTpl = new Roo.Template(
40259 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40260 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40261 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40264 var el = this.closeTpl.overwrite(td, {"text": text});
40265 var close = el.getElementsByTagName("div")[0];
40266 var inner = el.getElementsByTagName("em")[0];
40267 return {"el": el, "close": close, "inner": inner};
40270 // not sure what this is..
40271 // if(!this.tabTpl){
40272 //this.tabTpl = new Roo.Template(
40273 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40274 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40276 // this.tabTpl = new Roo.Template(
40277 // '<a href="#">' +
40278 // '<span unselectable="on"' +
40279 // (this.disableTooltips ? '' : ' title="{text}"') +
40280 // ' >{text}</span></a>'
40286 var template = tpl || this.tabTpl || false;
40289 template = new Roo.Template(
40290 Roo.bootstrap.version == 4 ?
40292 '<a class="nav-link" href="#" unselectable="on"' +
40293 (this.disableTooltips ? '' : ' title="{text}"') +
40296 '<a class="nav-link" href="#">' +
40297 '<span unselectable="on"' +
40298 (this.disableTooltips ? '' : ' title="{text}"') +
40299 ' >{text}</span></a>'
40304 switch (typeof(template)) {
40308 template = new Roo.Template(template);
40314 var el = template.overwrite(td, {"text": text});
40316 var inner = el.getElementsByTagName("span")[0];
40318 return {"el": el, "inner": inner};
40326 * @class Roo.TabPanelItem
40327 * @extends Roo.util.Observable
40328 * Represents an individual item (tab plus body) in a TabPanel.
40329 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40330 * @param {String} id The id of this TabPanelItem
40331 * @param {String} text The text for the tab of this TabPanelItem
40332 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40334 Roo.bootstrap.panel.TabItem = function(config){
40336 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40337 * @type Roo.TabPanel
40339 this.tabPanel = config.panel;
40341 * The id for this TabPanelItem
40344 this.id = config.id;
40346 this.disabled = false;
40348 this.text = config.text;
40350 this.loaded = false;
40351 this.closable = config.closable;
40354 * The body element for this TabPanelItem.
40355 * @type Roo.Element
40357 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40358 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40359 this.bodyEl.setStyle("display", "block");
40360 this.bodyEl.setStyle("zoom", "1");
40361 //this.hideAction();
40363 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40365 this.el = Roo.get(els.el);
40366 this.inner = Roo.get(els.inner, true);
40367 this.textEl = Roo.bootstrap.version == 4 ?
40368 this.el : Roo.get(this.el.dom.firstChild, true);
40370 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40371 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40374 // this.el.on("mousedown", this.onTabMouseDown, this);
40375 this.el.on("click", this.onTabClick, this);
40377 if(config.closable){
40378 var c = Roo.get(els.close, true);
40379 c.dom.title = this.closeText;
40380 c.addClassOnOver("close-over");
40381 c.on("click", this.closeClick, this);
40387 * Fires when this tab becomes the active tab.
40388 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40389 * @param {Roo.TabPanelItem} this
40393 * @event beforeclose
40394 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40395 * @param {Roo.TabPanelItem} this
40396 * @param {Object} e Set cancel to true on this object to cancel the close.
40398 "beforeclose": true,
40401 * Fires when this tab is closed.
40402 * @param {Roo.TabPanelItem} this
40406 * @event deactivate
40407 * Fires when this tab is no longer the active tab.
40408 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40409 * @param {Roo.TabPanelItem} this
40411 "deactivate" : true
40413 this.hidden = false;
40415 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40418 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40420 purgeListeners : function(){
40421 Roo.util.Observable.prototype.purgeListeners.call(this);
40422 this.el.removeAllListeners();
40425 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40428 this.status_node.addClass("active");
40431 this.tabPanel.stripWrap.repaint();
40433 this.fireEvent("activate", this.tabPanel, this);
40437 * Returns true if this tab is the active tab.
40438 * @return {Boolean}
40440 isActive : function(){
40441 return this.tabPanel.getActiveTab() == this;
40445 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40448 this.status_node.removeClass("active");
40450 this.fireEvent("deactivate", this.tabPanel, this);
40453 hideAction : function(){
40454 this.bodyEl.hide();
40455 this.bodyEl.setStyle("position", "absolute");
40456 this.bodyEl.setLeft("-20000px");
40457 this.bodyEl.setTop("-20000px");
40460 showAction : function(){
40461 this.bodyEl.setStyle("position", "relative");
40462 this.bodyEl.setTop("");
40463 this.bodyEl.setLeft("");
40464 this.bodyEl.show();
40468 * Set the tooltip for the tab.
40469 * @param {String} tooltip The tab's tooltip
40471 setTooltip : function(text){
40472 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40473 this.textEl.dom.qtip = text;
40474 this.textEl.dom.removeAttribute('title');
40476 this.textEl.dom.title = text;
40480 onTabClick : function(e){
40481 e.preventDefault();
40482 this.tabPanel.activate(this.id);
40485 onTabMouseDown : function(e){
40486 e.preventDefault();
40487 this.tabPanel.activate(this.id);
40490 getWidth : function(){
40491 return this.inner.getWidth();
40494 setWidth : function(width){
40495 var iwidth = width - this.linode.getPadding("lr");
40496 this.inner.setWidth(iwidth);
40497 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40498 this.linode.setWidth(width);
40502 * Show or hide the tab
40503 * @param {Boolean} hidden True to hide or false to show.
40505 setHidden : function(hidden){
40506 this.hidden = hidden;
40507 this.linode.setStyle("display", hidden ? "none" : "");
40511 * Returns true if this tab is "hidden"
40512 * @return {Boolean}
40514 isHidden : function(){
40515 return this.hidden;
40519 * Returns the text for this tab
40522 getText : function(){
40526 autoSize : function(){
40527 //this.el.beginMeasure();
40528 this.textEl.setWidth(1);
40530 * #2804 [new] Tabs in Roojs
40531 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40533 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40534 //this.el.endMeasure();
40538 * Sets the text for the tab (Note: this also sets the tooltip text)
40539 * @param {String} text The tab's text and tooltip
40541 setText : function(text){
40543 this.textEl.update(text);
40544 this.setTooltip(text);
40545 //if(!this.tabPanel.resizeTabs){
40546 // this.autoSize();
40550 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40552 activate : function(){
40553 this.tabPanel.activate(this.id);
40557 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40559 disable : function(){
40560 if(this.tabPanel.active != this){
40561 this.disabled = true;
40562 this.status_node.addClass("disabled");
40567 * Enables this TabPanelItem if it was previously disabled.
40569 enable : function(){
40570 this.disabled = false;
40571 this.status_node.removeClass("disabled");
40575 * Sets the content for this TabPanelItem.
40576 * @param {String} content The content
40577 * @param {Boolean} loadScripts true to look for and load scripts
40579 setContent : function(content, loadScripts){
40580 this.bodyEl.update(content, loadScripts);
40584 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40585 * @return {Roo.UpdateManager} The UpdateManager
40587 getUpdateManager : function(){
40588 return this.bodyEl.getUpdateManager();
40592 * Set a URL to be used to load the content for this TabPanelItem.
40593 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40594 * @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)
40595 * @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)
40596 * @return {Roo.UpdateManager} The UpdateManager
40598 setUrl : function(url, params, loadOnce){
40599 if(this.refreshDelegate){
40600 this.un('activate', this.refreshDelegate);
40602 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40603 this.on("activate", this.refreshDelegate);
40604 return this.bodyEl.getUpdateManager();
40608 _handleRefresh : function(url, params, loadOnce){
40609 if(!loadOnce || !this.loaded){
40610 var updater = this.bodyEl.getUpdateManager();
40611 updater.update(url, params, this._setLoaded.createDelegate(this));
40616 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40617 * Will fail silently if the setUrl method has not been called.
40618 * This does not activate the panel, just updates its content.
40620 refresh : function(){
40621 if(this.refreshDelegate){
40622 this.loaded = false;
40623 this.refreshDelegate();
40628 _setLoaded : function(){
40629 this.loaded = true;
40633 closeClick : function(e){
40636 this.fireEvent("beforeclose", this, o);
40637 if(o.cancel !== true){
40638 this.tabPanel.removeTab(this.id);
40642 * The text displayed in the tooltip for the close icon.
40645 closeText : "Close this tab"
40648 * This script refer to:
40649 * Title: International Telephone Input
40650 * Author: Jack O'Connor
40651 * Code version: v12.1.12
40652 * Availability: https://github.com/jackocnr/intl-tel-input.git
40655 Roo.bootstrap.PhoneInputData = function() {
40658 "Afghanistan (افغانستان)",
40663 "Albania (Shqipëri)",
40668 "Algeria (الجزائر)",
40693 "Antigua and Barbuda",
40703 "Armenia (Հայաստան)",
40719 "Austria (Österreich)",
40724 "Azerbaijan (Azərbaycan)",
40734 "Bahrain (البحرين)",
40739 "Bangladesh (বাংলাদেশ)",
40749 "Belarus (Беларусь)",
40754 "Belgium (België)",
40784 "Bosnia and Herzegovina (Босна и Херцеговина)",
40799 "British Indian Ocean Territory",
40804 "British Virgin Islands",
40814 "Bulgaria (България)",
40824 "Burundi (Uburundi)",
40829 "Cambodia (កម្ពុជា)",
40834 "Cameroon (Cameroun)",
40843 ["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"]
40846 "Cape Verde (Kabu Verdi)",
40851 "Caribbean Netherlands",
40862 "Central African Republic (République centrafricaine)",
40882 "Christmas Island",
40888 "Cocos (Keeling) Islands",
40899 "Comoros (جزر القمر)",
40904 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40909 "Congo (Republic) (Congo-Brazzaville)",
40929 "Croatia (Hrvatska)",
40950 "Czech Republic (Česká republika)",
40955 "Denmark (Danmark)",
40970 "Dominican Republic (República Dominicana)",
40974 ["809", "829", "849"]
40992 "Equatorial Guinea (Guinea Ecuatorial)",
41012 "Falkland Islands (Islas Malvinas)",
41017 "Faroe Islands (Føroyar)",
41038 "French Guiana (Guyane française)",
41043 "French Polynesia (Polynésie française)",
41058 "Georgia (საქართველო)",
41063 "Germany (Deutschland)",
41083 "Greenland (Kalaallit Nunaat)",
41120 "Guinea-Bissau (Guiné Bissau)",
41145 "Hungary (Magyarország)",
41150 "Iceland (Ísland)",
41170 "Iraq (العراق)",
41186 "Israel (ישראל)",
41213 "Jordan (الأردن)",
41218 "Kazakhstan (Казахстан)",
41239 "Kuwait (الكويت)",
41244 "Kyrgyzstan (Кыргызстан)",
41254 "Latvia (Latvija)",
41259 "Lebanon (لبنان)",
41274 "Libya (ليبيا)",
41284 "Lithuania (Lietuva)",
41299 "Macedonia (FYROM) (Македонија)",
41304 "Madagascar (Madagasikara)",
41334 "Marshall Islands",
41344 "Mauritania (موريتانيا)",
41349 "Mauritius (Moris)",
41370 "Moldova (Republica Moldova)",
41380 "Mongolia (Монгол)",
41385 "Montenegro (Crna Gora)",
41395 "Morocco (المغرب)",
41401 "Mozambique (Moçambique)",
41406 "Myanmar (Burma) (မြန်မာ)",
41411 "Namibia (Namibië)",
41426 "Netherlands (Nederland)",
41431 "New Caledonia (Nouvelle-Calédonie)",
41466 "North Korea (조선 민주주의 인민 공화국)",
41471 "Northern Mariana Islands",
41487 "Pakistan (پاکستان)",
41497 "Palestine (فلسطين)",
41507 "Papua New Guinea",
41549 "Réunion (La Réunion)",
41555 "Romania (România)",
41571 "Saint Barthélemy",
41582 "Saint Kitts and Nevis",
41592 "Saint Martin (Saint-Martin (partie française))",
41598 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41603 "Saint Vincent and the Grenadines",
41618 "São Tomé and Príncipe (São Tomé e Príncipe)",
41623 "Saudi Arabia (المملكة العربية السعودية)",
41628 "Senegal (Sénégal)",
41658 "Slovakia (Slovensko)",
41663 "Slovenia (Slovenija)",
41673 "Somalia (Soomaaliya)",
41683 "South Korea (대한민국)",
41688 "South Sudan (جنوب السودان)",
41698 "Sri Lanka (ශ්රී ලංකාව)",
41703 "Sudan (السودان)",
41713 "Svalbard and Jan Mayen",
41724 "Sweden (Sverige)",
41729 "Switzerland (Schweiz)",
41734 "Syria (سوريا)",
41779 "Trinidad and Tobago",
41784 "Tunisia (تونس)",
41789 "Turkey (Türkiye)",
41799 "Turks and Caicos Islands",
41809 "U.S. Virgin Islands",
41819 "Ukraine (Україна)",
41824 "United Arab Emirates (الإمارات العربية المتحدة)",
41846 "Uzbekistan (Oʻzbekiston)",
41856 "Vatican City (Città del Vaticano)",
41867 "Vietnam (Việt Nam)",
41872 "Wallis and Futuna (Wallis-et-Futuna)",
41877 "Western Sahara (الصحراء الغربية)",
41883 "Yemen (اليمن)",
41907 * This script refer to:
41908 * Title: International Telephone Input
41909 * Author: Jack O'Connor
41910 * Code version: v12.1.12
41911 * Availability: https://github.com/jackocnr/intl-tel-input.git
41915 * @class Roo.bootstrap.PhoneInput
41916 * @extends Roo.bootstrap.TriggerField
41917 * An input with International dial-code selection
41919 * @cfg {String} defaultDialCode default '+852'
41920 * @cfg {Array} preferedCountries default []
41923 * Create a new PhoneInput.
41924 * @param {Object} config Configuration options
41927 Roo.bootstrap.PhoneInput = function(config) {
41928 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41931 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41933 listWidth: undefined,
41935 selectedClass: 'active',
41937 invalidClass : "has-warning",
41939 validClass: 'has-success',
41941 allowed: '0123456789',
41946 * @cfg {String} defaultDialCode The default dial code when initializing the input
41948 defaultDialCode: '+852',
41951 * @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
41953 preferedCountries: false,
41955 getAutoCreate : function()
41957 var data = Roo.bootstrap.PhoneInputData();
41958 var align = this.labelAlign || this.parentLabelAlign();
41961 this.allCountries = [];
41962 this.dialCodeMapping = [];
41964 for (var i = 0; i < data.length; i++) {
41966 this.allCountries[i] = {
41970 priority: c[3] || 0,
41971 areaCodes: c[4] || null
41973 this.dialCodeMapping[c[2]] = {
41976 priority: c[3] || 0,
41977 areaCodes: c[4] || null
41989 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41990 maxlength: this.max_length,
41991 cls : 'form-control tel-input',
41992 autocomplete: 'new-password'
41995 var hiddenInput = {
41998 cls: 'hidden-tel-input'
42002 hiddenInput.name = this.name;
42005 if (this.disabled) {
42006 input.disabled = true;
42009 var flag_container = {
42026 cls: this.hasFeedback ? 'has-feedback' : '',
42032 cls: 'dial-code-holder',
42039 cls: 'roo-select2-container input-group',
42046 if (this.fieldLabel.length) {
42049 tooltip: 'This field is required'
42055 cls: 'control-label',
42061 html: this.fieldLabel
42064 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42070 if(this.indicatorpos == 'right') {
42071 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42078 if(align == 'left') {
42086 if(this.labelWidth > 12){
42087 label.style = "width: " + this.labelWidth + 'px';
42089 if(this.labelWidth < 13 && this.labelmd == 0){
42090 this.labelmd = this.labelWidth;
42092 if(this.labellg > 0){
42093 label.cls += ' col-lg-' + this.labellg;
42094 input.cls += ' col-lg-' + (12 - this.labellg);
42096 if(this.labelmd > 0){
42097 label.cls += ' col-md-' + this.labelmd;
42098 container.cls += ' col-md-' + (12 - this.labelmd);
42100 if(this.labelsm > 0){
42101 label.cls += ' col-sm-' + this.labelsm;
42102 container.cls += ' col-sm-' + (12 - this.labelsm);
42104 if(this.labelxs > 0){
42105 label.cls += ' col-xs-' + this.labelxs;
42106 container.cls += ' col-xs-' + (12 - this.labelxs);
42116 var settings = this;
42118 ['xs','sm','md','lg'].map(function(size){
42119 if (settings[size]) {
42120 cfg.cls += ' col-' + size + '-' + settings[size];
42124 this.store = new Roo.data.Store({
42125 proxy : new Roo.data.MemoryProxy({}),
42126 reader : new Roo.data.JsonReader({
42137 'name' : 'dialCode',
42141 'name' : 'priority',
42145 'name' : 'areaCodes',
42152 if(!this.preferedCountries) {
42153 this.preferedCountries = [
42160 var p = this.preferedCountries.reverse();
42163 for (var i = 0; i < p.length; i++) {
42164 for (var j = 0; j < this.allCountries.length; j++) {
42165 if(this.allCountries[j].iso2 == p[i]) {
42166 var t = this.allCountries[j];
42167 this.allCountries.splice(j,1);
42168 this.allCountries.unshift(t);
42174 this.store.proxy.data = {
42176 data: this.allCountries
42182 initEvents : function()
42185 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42187 this.indicator = this.indicatorEl();
42188 this.flag = this.flagEl();
42189 this.dialCodeHolder = this.dialCodeHolderEl();
42191 this.trigger = this.el.select('div.flag-box',true).first();
42192 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42197 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42198 _this.list.setWidth(lw);
42201 this.list.on('mouseover', this.onViewOver, this);
42202 this.list.on('mousemove', this.onViewMove, this);
42203 this.inputEl().on("keyup", this.onKeyUp, this);
42204 this.inputEl().on("keypress", this.onKeyPress, this);
42206 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42208 this.view = new Roo.View(this.list, this.tpl, {
42209 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42212 this.view.on('click', this.onViewClick, this);
42213 this.setValue(this.defaultDialCode);
42216 onTriggerClick : function(e)
42218 Roo.log('trigger click');
42223 if(this.isExpanded()){
42225 this.hasFocus = false;
42227 this.store.load({});
42228 this.hasFocus = true;
42233 isExpanded : function()
42235 return this.list.isVisible();
42238 collapse : function()
42240 if(!this.isExpanded()){
42244 Roo.get(document).un('mousedown', this.collapseIf, this);
42245 Roo.get(document).un('mousewheel', this.collapseIf, this);
42246 this.fireEvent('collapse', this);
42250 expand : function()
42254 if(this.isExpanded() || !this.hasFocus){
42258 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42259 this.list.setWidth(lw);
42262 this.restrictHeight();
42264 Roo.get(document).on('mousedown', this.collapseIf, this);
42265 Roo.get(document).on('mousewheel', this.collapseIf, this);
42267 this.fireEvent('expand', this);
42270 restrictHeight : function()
42272 this.list.alignTo(this.inputEl(), this.listAlign);
42273 this.list.alignTo(this.inputEl(), this.listAlign);
42276 onViewOver : function(e, t)
42278 if(this.inKeyMode){
42281 var item = this.view.findItemFromChild(t);
42284 var index = this.view.indexOf(item);
42285 this.select(index, false);
42290 onViewClick : function(view, doFocus, el, e)
42292 var index = this.view.getSelectedIndexes()[0];
42294 var r = this.store.getAt(index);
42297 this.onSelect(r, index);
42299 if(doFocus !== false && !this.blockFocus){
42300 this.inputEl().focus();
42304 onViewMove : function(e, t)
42306 this.inKeyMode = false;
42309 select : function(index, scrollIntoView)
42311 this.selectedIndex = index;
42312 this.view.select(index);
42313 if(scrollIntoView !== false){
42314 var el = this.view.getNode(index);
42316 this.list.scrollChildIntoView(el, false);
42321 createList : function()
42323 this.list = Roo.get(document.body).createChild({
42325 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42326 style: 'display:none'
42329 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42332 collapseIf : function(e)
42334 var in_combo = e.within(this.el);
42335 var in_list = e.within(this.list);
42336 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42338 if (in_combo || in_list || is_list) {
42344 onSelect : function(record, index)
42346 if(this.fireEvent('beforeselect', this, record, index) !== false){
42348 this.setFlagClass(record.data.iso2);
42349 this.setDialCode(record.data.dialCode);
42350 this.hasFocus = false;
42352 this.fireEvent('select', this, record, index);
42356 flagEl : function()
42358 var flag = this.el.select('div.flag',true).first();
42365 dialCodeHolderEl : function()
42367 var d = this.el.select('input.dial-code-holder',true).first();
42374 setDialCode : function(v)
42376 this.dialCodeHolder.dom.value = '+'+v;
42379 setFlagClass : function(n)
42381 this.flag.dom.className = 'flag '+n;
42384 getValue : function()
42386 var v = this.inputEl().getValue();
42387 if(this.dialCodeHolder) {
42388 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42393 setValue : function(v)
42395 var d = this.getDialCode(v);
42397 //invalid dial code
42398 if(v.length == 0 || !d || d.length == 0) {
42400 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42401 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42407 this.setFlagClass(this.dialCodeMapping[d].iso2);
42408 this.setDialCode(d);
42409 this.inputEl().dom.value = v.replace('+'+d,'');
42410 this.hiddenEl().dom.value = this.getValue();
42415 getDialCode : function(v)
42419 if (v.length == 0) {
42420 return this.dialCodeHolder.dom.value;
42424 if (v.charAt(0) != "+") {
42427 var numericChars = "";
42428 for (var i = 1; i < v.length; i++) {
42429 var c = v.charAt(i);
42432 if (this.dialCodeMapping[numericChars]) {
42433 dialCode = v.substr(1, i);
42435 if (numericChars.length == 4) {
42445 this.setValue(this.defaultDialCode);
42449 hiddenEl : function()
42451 return this.el.select('input.hidden-tel-input',true).first();
42454 // after setting val
42455 onKeyUp : function(e){
42456 this.setValue(this.getValue());
42459 onKeyPress : function(e){
42460 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42467 * @class Roo.bootstrap.MoneyField
42468 * @extends Roo.bootstrap.ComboBox
42469 * Bootstrap MoneyField class
42472 * Create a new MoneyField.
42473 * @param {Object} config Configuration options
42476 Roo.bootstrap.MoneyField = function(config) {
42478 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42482 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42485 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42487 allowDecimals : true,
42489 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42491 decimalSeparator : ".",
42493 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42495 decimalPrecision : 0,
42497 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42499 allowNegative : true,
42501 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42505 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42507 minValue : Number.NEGATIVE_INFINITY,
42509 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42511 maxValue : Number.MAX_VALUE,
42513 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42515 minText : "The minimum value for this field is {0}",
42517 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42519 maxText : "The maximum value for this field is {0}",
42521 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42522 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42524 nanText : "{0} is not a valid number",
42526 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42530 * @cfg {String} defaults currency of the MoneyField
42531 * value should be in lkey
42533 defaultCurrency : false,
42535 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42537 thousandsDelimiter : false,
42539 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42550 getAutoCreate : function()
42552 var align = this.labelAlign || this.parentLabelAlign();
42564 cls : 'form-control roo-money-amount-input',
42565 autocomplete: 'new-password'
42568 var hiddenInput = {
42572 cls: 'hidden-number-input'
42575 if(this.max_length) {
42576 input.maxlength = this.max_length;
42580 hiddenInput.name = this.name;
42583 if (this.disabled) {
42584 input.disabled = true;
42587 var clg = 12 - this.inputlg;
42588 var cmd = 12 - this.inputmd;
42589 var csm = 12 - this.inputsm;
42590 var cxs = 12 - this.inputxs;
42594 cls : 'row roo-money-field',
42598 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42602 cls: 'roo-select2-container input-group',
42606 cls : 'form-control roo-money-currency-input',
42607 autocomplete: 'new-password',
42609 name : this.currencyName
42613 cls : 'input-group-addon',
42627 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42631 cls: this.hasFeedback ? 'has-feedback' : '',
42642 if (this.fieldLabel.length) {
42645 tooltip: 'This field is required'
42651 cls: 'control-label',
42657 html: this.fieldLabel
42660 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42666 if(this.indicatorpos == 'right') {
42667 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42674 if(align == 'left') {
42682 if(this.labelWidth > 12){
42683 label.style = "width: " + this.labelWidth + 'px';
42685 if(this.labelWidth < 13 && this.labelmd == 0){
42686 this.labelmd = this.labelWidth;
42688 if(this.labellg > 0){
42689 label.cls += ' col-lg-' + this.labellg;
42690 input.cls += ' col-lg-' + (12 - this.labellg);
42692 if(this.labelmd > 0){
42693 label.cls += ' col-md-' + this.labelmd;
42694 container.cls += ' col-md-' + (12 - this.labelmd);
42696 if(this.labelsm > 0){
42697 label.cls += ' col-sm-' + this.labelsm;
42698 container.cls += ' col-sm-' + (12 - this.labelsm);
42700 if(this.labelxs > 0){
42701 label.cls += ' col-xs-' + this.labelxs;
42702 container.cls += ' col-xs-' + (12 - this.labelxs);
42713 var settings = this;
42715 ['xs','sm','md','lg'].map(function(size){
42716 if (settings[size]) {
42717 cfg.cls += ' col-' + size + '-' + settings[size];
42724 initEvents : function()
42726 this.indicator = this.indicatorEl();
42728 this.initCurrencyEvent();
42730 this.initNumberEvent();
42733 initCurrencyEvent : function()
42736 throw "can not find store for combo";
42739 this.store = Roo.factory(this.store, Roo.data);
42740 this.store.parent = this;
42744 this.triggerEl = this.el.select('.input-group-addon', true).first();
42746 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42751 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42752 _this.list.setWidth(lw);
42755 this.list.on('mouseover', this.onViewOver, this);
42756 this.list.on('mousemove', this.onViewMove, this);
42757 this.list.on('scroll', this.onViewScroll, this);
42760 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42763 this.view = new Roo.View(this.list, this.tpl, {
42764 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42767 this.view.on('click', this.onViewClick, this);
42769 this.store.on('beforeload', this.onBeforeLoad, this);
42770 this.store.on('load', this.onLoad, this);
42771 this.store.on('loadexception', this.onLoadException, this);
42773 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42774 "up" : function(e){
42775 this.inKeyMode = true;
42779 "down" : function(e){
42780 if(!this.isExpanded()){
42781 this.onTriggerClick();
42783 this.inKeyMode = true;
42788 "enter" : function(e){
42791 if(this.fireEvent("specialkey", this, e)){
42792 this.onViewClick(false);
42798 "esc" : function(e){
42802 "tab" : function(e){
42805 if(this.fireEvent("specialkey", this, e)){
42806 this.onViewClick(false);
42814 doRelay : function(foo, bar, hname){
42815 if(hname == 'down' || this.scope.isExpanded()){
42816 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42824 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42828 initNumberEvent : function(e)
42830 this.inputEl().on("keydown" , this.fireKey, this);
42831 this.inputEl().on("focus", this.onFocus, this);
42832 this.inputEl().on("blur", this.onBlur, this);
42834 this.inputEl().relayEvent('keyup', this);
42836 if(this.indicator){
42837 this.indicator.addClass('invisible');
42840 this.originalValue = this.getValue();
42842 if(this.validationEvent == 'keyup'){
42843 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42844 this.inputEl().on('keyup', this.filterValidation, this);
42846 else if(this.validationEvent !== false){
42847 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42850 if(this.selectOnFocus){
42851 this.on("focus", this.preFocus, this);
42854 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42855 this.inputEl().on("keypress", this.filterKeys, this);
42857 this.inputEl().relayEvent('keypress', this);
42860 var allowed = "0123456789";
42862 if(this.allowDecimals){
42863 allowed += this.decimalSeparator;
42866 if(this.allowNegative){
42870 if(this.thousandsDelimiter) {
42874 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42876 var keyPress = function(e){
42878 var k = e.getKey();
42880 var c = e.getCharCode();
42883 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42884 allowed.indexOf(String.fromCharCode(c)) === -1
42890 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42894 if(allowed.indexOf(String.fromCharCode(c)) === -1){
42899 this.inputEl().on("keypress", keyPress, this);
42903 onTriggerClick : function(e)
42910 this.loadNext = false;
42912 if(this.isExpanded()){
42917 this.hasFocus = true;
42919 if(this.triggerAction == 'all') {
42920 this.doQuery(this.allQuery, true);
42924 this.doQuery(this.getRawValue());
42927 getCurrency : function()
42929 var v = this.currencyEl().getValue();
42934 restrictHeight : function()
42936 this.list.alignTo(this.currencyEl(), this.listAlign);
42937 this.list.alignTo(this.currencyEl(), this.listAlign);
42940 onViewClick : function(view, doFocus, el, e)
42942 var index = this.view.getSelectedIndexes()[0];
42944 var r = this.store.getAt(index);
42947 this.onSelect(r, index);
42951 onSelect : function(record, index){
42953 if(this.fireEvent('beforeselect', this, record, index) !== false){
42955 this.setFromCurrencyData(index > -1 ? record.data : false);
42959 this.fireEvent('select', this, record, index);
42963 setFromCurrencyData : function(o)
42967 this.lastCurrency = o;
42969 if (this.currencyField) {
42970 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42972 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
42975 this.lastSelectionText = currency;
42977 //setting default currency
42978 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42979 this.setCurrency(this.defaultCurrency);
42983 this.setCurrency(currency);
42986 setFromData : function(o)
42990 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42992 this.setFromCurrencyData(c);
42997 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42999 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43002 this.setValue(value);
43006 setCurrency : function(v)
43008 this.currencyValue = v;
43011 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43016 setValue : function(v)
43018 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43024 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43026 this.inputEl().dom.value = (v == '') ? '' :
43027 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43029 if(!this.allowZero && v === '0') {
43030 this.hiddenEl().dom.value = '';
43031 this.inputEl().dom.value = '';
43038 getRawValue : function()
43040 var v = this.inputEl().getValue();
43045 getValue : function()
43047 return this.fixPrecision(this.parseValue(this.getRawValue()));
43050 parseValue : function(value)
43052 if(this.thousandsDelimiter) {
43054 r = new RegExp(",", "g");
43055 value = value.replace(r, "");
43058 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43059 return isNaN(value) ? '' : value;
43063 fixPrecision : function(value)
43065 if(this.thousandsDelimiter) {
43067 r = new RegExp(",", "g");
43068 value = value.replace(r, "");
43071 var nan = isNaN(value);
43073 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43074 return nan ? '' : value;
43076 return parseFloat(value).toFixed(this.decimalPrecision);
43079 decimalPrecisionFcn : function(v)
43081 return Math.floor(v);
43084 validateValue : function(value)
43086 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43090 var num = this.parseValue(value);
43093 this.markInvalid(String.format(this.nanText, value));
43097 if(num < this.minValue){
43098 this.markInvalid(String.format(this.minText, this.minValue));
43102 if(num > this.maxValue){
43103 this.markInvalid(String.format(this.maxText, this.maxValue));
43110 validate : function()
43112 if(this.disabled || this.allowBlank){
43117 var currency = this.getCurrency();
43119 if(this.validateValue(this.getRawValue()) && currency.length){
43124 this.markInvalid();
43128 getName: function()
43133 beforeBlur : function()
43139 var v = this.parseValue(this.getRawValue());
43146 onBlur : function()
43150 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43151 //this.el.removeClass(this.focusClass);
43154 this.hasFocus = false;
43156 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43160 var v = this.getValue();
43162 if(String(v) !== String(this.startValue)){
43163 this.fireEvent('change', this, v, this.startValue);
43166 this.fireEvent("blur", this);
43169 inputEl : function()
43171 return this.el.select('.roo-money-amount-input', true).first();
43174 currencyEl : function()
43176 return this.el.select('.roo-money-currency-input', true).first();
43179 hiddenEl : function()
43181 return this.el.select('input.hidden-number-input',true).first();
43185 * @class Roo.bootstrap.BezierSignature
43186 * @extends Roo.bootstrap.Component
43187 * Bootstrap BezierSignature class
43188 * This script refer to:
43189 * Title: Signature Pad
43191 * Availability: https://github.com/szimek/signature_pad
43194 * Create a new BezierSignature
43195 * @param {Object} config The config object
43198 Roo.bootstrap.BezierSignature = function(config){
43199 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43205 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43212 mouse_btn_down: true,
43215 * @cfg {int} canvas height
43217 canvas_height: '200px',
43220 * @cfg {float|function} Radius of a single dot.
43225 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43230 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43235 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43240 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43245 * @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.
43247 bg_color: 'rgba(0, 0, 0, 0)',
43250 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43252 dot_color: 'black',
43255 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43257 velocity_filter_weight: 0.7,
43260 * @cfg {function} Callback when stroke begin.
43265 * @cfg {function} Callback when stroke end.
43269 getAutoCreate : function()
43271 var cls = 'roo-signature column';
43274 cls += ' ' + this.cls;
43284 for(var i = 0; i < col_sizes.length; i++) {
43285 if(this[col_sizes[i]]) {
43286 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43296 cls: 'roo-signature-body',
43300 cls: 'roo-signature-body-canvas',
43301 height: this.canvas_height,
43302 width: this.canvas_width
43309 style: 'display: none'
43317 initEvents: function()
43319 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43321 var canvas = this.canvasEl();
43323 // mouse && touch event swapping...
43324 canvas.dom.style.touchAction = 'none';
43325 canvas.dom.style.msTouchAction = 'none';
43327 this.mouse_btn_down = false;
43328 canvas.on('mousedown', this._handleMouseDown, this);
43329 canvas.on('mousemove', this._handleMouseMove, this);
43330 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43332 if (window.PointerEvent) {
43333 canvas.on('pointerdown', this._handleMouseDown, this);
43334 canvas.on('pointermove', this._handleMouseMove, this);
43335 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43338 if ('ontouchstart' in window) {
43339 canvas.on('touchstart', this._handleTouchStart, this);
43340 canvas.on('touchmove', this._handleTouchMove, this);
43341 canvas.on('touchend', this._handleTouchEnd, this);
43344 Roo.EventManager.onWindowResize(this.resize, this, true);
43346 // file input event
43347 this.fileEl().on('change', this.uploadImage, this);
43354 resize: function(){
43356 var canvas = this.canvasEl().dom;
43357 var ctx = this.canvasElCtx();
43358 var img_data = false;
43360 if(canvas.width > 0) {
43361 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43363 // setting canvas width will clean img data
43366 var style = window.getComputedStyle ?
43367 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43369 var padding_left = parseInt(style.paddingLeft) || 0;
43370 var padding_right = parseInt(style.paddingRight) || 0;
43372 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43375 ctx.putImageData(img_data, 0, 0);
43379 _handleMouseDown: function(e)
43381 if (e.browserEvent.which === 1) {
43382 this.mouse_btn_down = true;
43383 this.strokeBegin(e);
43387 _handleMouseMove: function (e)
43389 if (this.mouse_btn_down) {
43390 this.strokeMoveUpdate(e);
43394 _handleMouseUp: function (e)
43396 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43397 this.mouse_btn_down = false;
43402 _handleTouchStart: function (e) {
43404 e.preventDefault();
43405 if (e.browserEvent.targetTouches.length === 1) {
43406 // var touch = e.browserEvent.changedTouches[0];
43407 // this.strokeBegin(touch);
43409 this.strokeBegin(e); // assume e catching the correct xy...
43413 _handleTouchMove: function (e) {
43414 e.preventDefault();
43415 // var touch = event.targetTouches[0];
43416 // _this._strokeMoveUpdate(touch);
43417 this.strokeMoveUpdate(e);
43420 _handleTouchEnd: function (e) {
43421 var wasCanvasTouched = e.target === this.canvasEl().dom;
43422 if (wasCanvasTouched) {
43423 e.preventDefault();
43424 // var touch = event.changedTouches[0];
43425 // _this._strokeEnd(touch);
43430 reset: function () {
43431 this._lastPoints = [];
43432 this._lastVelocity = 0;
43433 this._lastWidth = (this.min_width + this.max_width) / 2;
43434 this.canvasElCtx().fillStyle = this.dot_color;
43437 strokeMoveUpdate: function(e)
43439 this.strokeUpdate(e);
43441 if (this.throttle) {
43442 this.throttleStroke(this.strokeUpdate, this.throttle);
43445 this.strokeUpdate(e);
43449 strokeBegin: function(e)
43451 var newPointGroup = {
43452 color: this.dot_color,
43456 if (typeof this.onBegin === 'function') {
43460 this.curve_data.push(newPointGroup);
43462 this.strokeUpdate(e);
43465 strokeUpdate: function(e)
43467 var rect = this.canvasEl().dom.getBoundingClientRect();
43468 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43469 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43470 var lastPoints = lastPointGroup.points;
43471 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43472 var isLastPointTooClose = lastPoint
43473 ? point.distanceTo(lastPoint) <= this.min_distance
43475 var color = lastPointGroup.color;
43476 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43477 var curve = this.addPoint(point);
43479 this.drawDot({color: color, point: point});
43482 this.drawCurve({color: color, curve: curve});
43492 strokeEnd: function(e)
43494 this.strokeUpdate(e);
43495 if (typeof this.onEnd === 'function') {
43500 addPoint: function (point) {
43501 var _lastPoints = this._lastPoints;
43502 _lastPoints.push(point);
43503 if (_lastPoints.length > 2) {
43504 if (_lastPoints.length === 3) {
43505 _lastPoints.unshift(_lastPoints[0]);
43507 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43508 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43509 _lastPoints.shift();
43515 calculateCurveWidths: function (startPoint, endPoint) {
43516 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43517 (1 - this.velocity_filter_weight) * this._lastVelocity;
43519 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43522 start: this._lastWidth
43525 this._lastVelocity = velocity;
43526 this._lastWidth = newWidth;
43530 drawDot: function (_a) {
43531 var color = _a.color, point = _a.point;
43532 var ctx = this.canvasElCtx();
43533 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43535 this.drawCurveSegment(point.x, point.y, width);
43537 ctx.fillStyle = color;
43541 drawCurve: function (_a) {
43542 var color = _a.color, curve = _a.curve;
43543 var ctx = this.canvasElCtx();
43544 var widthDelta = curve.endWidth - curve.startWidth;
43545 var drawSteps = Math.floor(curve.length()) * 2;
43547 ctx.fillStyle = color;
43548 for (var i = 0; i < drawSteps; i += 1) {
43549 var t = i / drawSteps;
43555 var x = uuu * curve.startPoint.x;
43556 x += 3 * uu * t * curve.control1.x;
43557 x += 3 * u * tt * curve.control2.x;
43558 x += ttt * curve.endPoint.x;
43559 var y = uuu * curve.startPoint.y;
43560 y += 3 * uu * t * curve.control1.y;
43561 y += 3 * u * tt * curve.control2.y;
43562 y += ttt * curve.endPoint.y;
43563 var width = curve.startWidth + ttt * widthDelta;
43564 this.drawCurveSegment(x, y, width);
43570 drawCurveSegment: function (x, y, width) {
43571 var ctx = this.canvasElCtx();
43573 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43574 this.is_empty = false;
43579 var ctx = this.canvasElCtx();
43580 var canvas = this.canvasEl().dom;
43581 ctx.fillStyle = this.bg_color;
43582 ctx.clearRect(0, 0, canvas.width, canvas.height);
43583 ctx.fillRect(0, 0, canvas.width, canvas.height);
43584 this.curve_data = [];
43586 this.is_empty = true;
43591 return this.el.select('input',true).first();
43594 canvasEl: function()
43596 return this.el.select('canvas',true).first();
43599 canvasElCtx: function()
43601 return this.el.select('canvas',true).first().dom.getContext('2d');
43604 getImage: function(type)
43606 if(this.is_empty) {
43611 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43614 drawFromImage: function(img_src)
43616 var img = new Image();
43618 img.onload = function(){
43619 this.canvasElCtx().drawImage(img, 0, 0);
43624 this.is_empty = false;
43627 selectImage: function()
43629 this.fileEl().dom.click();
43632 uploadImage: function(e)
43634 var reader = new FileReader();
43636 reader.onload = function(e){
43637 var img = new Image();
43638 img.onload = function(){
43640 this.canvasElCtx().drawImage(img, 0, 0);
43642 img.src = e.target.result;
43645 reader.readAsDataURL(e.target.files[0]);
43648 // Bezier Point Constructor
43649 Point: (function () {
43650 function Point(x, y, time) {
43653 this.time = time || Date.now();
43655 Point.prototype.distanceTo = function (start) {
43656 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43658 Point.prototype.equals = function (other) {
43659 return this.x === other.x && this.y === other.y && this.time === other.time;
43661 Point.prototype.velocityFrom = function (start) {
43662 return this.time !== start.time
43663 ? this.distanceTo(start) / (this.time - start.time)
43670 // Bezier Constructor
43671 Bezier: (function () {
43672 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43673 this.startPoint = startPoint;
43674 this.control2 = control2;
43675 this.control1 = control1;
43676 this.endPoint = endPoint;
43677 this.startWidth = startWidth;
43678 this.endWidth = endWidth;
43680 Bezier.fromPoints = function (points, widths, scope) {
43681 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43682 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43683 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43685 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43686 var dx1 = s1.x - s2.x;
43687 var dy1 = s1.y - s2.y;
43688 var dx2 = s2.x - s3.x;
43689 var dy2 = s2.y - s3.y;
43690 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43691 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43692 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43693 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43694 var dxm = m1.x - m2.x;
43695 var dym = m1.y - m2.y;
43696 var k = l2 / (l1 + l2);
43697 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43698 var tx = s2.x - cm.x;
43699 var ty = s2.y - cm.y;
43701 c1: new scope.Point(m1.x + tx, m1.y + ty),
43702 c2: new scope.Point(m2.x + tx, m2.y + ty)
43705 Bezier.prototype.length = function () {
43710 for (var i = 0; i <= steps; i += 1) {
43712 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43713 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43715 var xdiff = cx - px;
43716 var ydiff = cy - py;
43717 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43724 Bezier.prototype.point = function (t, start, c1, c2, end) {
43725 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43726 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43727 + (3.0 * c2 * (1.0 - t) * t * t)
43728 + (end * t * t * t);
43733 throttleStroke: function(fn, wait) {
43734 if (wait === void 0) { wait = 250; }
43736 var timeout = null;
43740 var later = function () {
43741 previous = Date.now();
43743 result = fn.apply(storedContext, storedArgs);
43745 storedContext = null;
43749 return function wrapper() {
43751 for (var _i = 0; _i < arguments.length; _i++) {
43752 args[_i] = arguments[_i];
43754 var now = Date.now();
43755 var remaining = wait - (now - previous);
43756 storedContext = this;
43758 if (remaining <= 0 || remaining > wait) {
43760 clearTimeout(timeout);
43764 result = fn.apply(storedContext, storedArgs);
43766 storedContext = null;
43770 else if (!timeout) {
43771 timeout = window.setTimeout(later, remaining);