2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2683 if (this.headerContainerEl) {
2684 this.headerContainerEl.dom.innerHTML = html;
2694 * Card header - holder for the card header elements.
2699 * @class Roo.bootstrap.CardHeader
2700 * @extends Roo.bootstrap.Element
2701 * Bootstrap CardHeader class
2703 * Create a new Card Header - that you can embed children into
2704 * @param {Object} config The config object
2707 Roo.bootstrap.CardHeader = function(config){
2708 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2714 container_method : 'getCardHeader'
2727 * Card footer - holder for the card footer elements.
2732 * @class Roo.bootstrap.CardFooter
2733 * @extends Roo.bootstrap.Element
2734 * Bootstrap CardFooter class
2736 * Create a new Card Footer - that you can embed children into
2737 * @param {Object} config The config object
2740 Roo.bootstrap.CardFooter = function(config){
2741 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2747 container_method : 'getCardFooter'
2760 * Card header - holder for the card header elements.
2765 * @class Roo.bootstrap.CardImageTop
2766 * @extends Roo.bootstrap.Element
2767 * Bootstrap CardImageTop class
2769 * Create a new Card Image Top container
2770 * @param {Object} config The config object
2773 Roo.bootstrap.CardImageTop = function(config){
2774 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2780 container_method : 'getCardImageTop'
2798 * @class Roo.bootstrap.Img
2799 * @extends Roo.bootstrap.Component
2800 * Bootstrap Img class
2801 * @cfg {Boolean} imgResponsive false | true
2802 * @cfg {String} border rounded | circle | thumbnail
2803 * @cfg {String} src image source
2804 * @cfg {String} alt image alternative text
2805 * @cfg {String} href a tag href
2806 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807 * @cfg {String} xsUrl xs image source
2808 * @cfg {String} smUrl sm image source
2809 * @cfg {String} mdUrl md image source
2810 * @cfg {String} lgUrl lg image source
2813 * Create a new Input
2814 * @param {Object} config The config object
2817 Roo.bootstrap.Img = function(config){
2818 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2824 * The img click event for the img.
2825 * @param {Roo.EventObject} e
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2833 imgResponsive: true,
2843 getAutoCreate : function()
2845 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846 return this.createSingleImg();
2851 cls: 'roo-image-responsive-group',
2856 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2858 if(!_this[size + 'Url']){
2864 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865 html: _this.html || cfg.html,
2866 src: _this[size + 'Url']
2869 img.cls += ' roo-image-responsive-' + size;
2871 var s = ['xs', 'sm', 'md', 'lg'];
2873 s.splice(s.indexOf(size), 1);
2875 Roo.each(s, function(ss){
2876 img.cls += ' hidden-' + ss;
2879 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880 cfg.cls += ' img-' + _this.border;
2884 cfg.alt = _this.alt;
2897 a.target = _this.target;
2901 cfg.cn.push((_this.href) ? a : img);
2908 createSingleImg : function()
2912 cls: (this.imgResponsive) ? 'img-responsive' : '',
2914 src : 'about:blank' // just incase src get's set to undefined?!?
2917 cfg.html = this.html || cfg.html;
2919 cfg.src = this.src || cfg.src;
2921 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922 cfg.cls += ' img-' + this.border;
2939 a.target = this.target;
2944 return (this.href) ? a : cfg;
2947 initEvents: function()
2950 this.el.on('click', this.onClick, this);
2955 onClick : function(e)
2957 Roo.log('img onclick');
2958 this.fireEvent('click', this, e);
2961 * Sets the url of the image - used to update it
2962 * @param {String} url the url of the image
2965 setSrc : function(url)
2969 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970 this.el.dom.src = url;
2974 this.el.select('img', true).first().dom.src = url;
2990 * @class Roo.bootstrap.Link
2991 * @extends Roo.bootstrap.Component
2992 * Bootstrap Link Class
2993 * @cfg {String} alt image alternative text
2994 * @cfg {String} href a tag href
2995 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996 * @cfg {String} html the content of the link.
2997 * @cfg {String} anchor name for the anchor link
2998 * @cfg {String} fa - favicon
3000 * @cfg {Boolean} preventDefault (true | false) default false
3004 * Create a new Input
3005 * @param {Object} config The config object
3008 Roo.bootstrap.Link = function(config){
3009 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3015 * The img click event for the img.
3016 * @param {Roo.EventObject} e
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3026 preventDefault: false,
3032 getAutoCreate : function()
3034 var html = this.html || '';
3036 if (this.fa !== false) {
3037 html = '<i class="fa fa-' + this.fa + '"></i>';
3042 // anchor's do not require html/href...
3043 if (this.anchor === false) {
3045 cfg.href = this.href || '#';
3047 cfg.name = this.anchor;
3048 if (this.html !== false || this.fa !== false) {
3051 if (this.href !== false) {
3052 cfg.href = this.href;
3056 if(this.alt !== false){
3061 if(this.target !== false) {
3062 cfg.target = this.target;
3068 initEvents: function() {
3070 if(!this.href || this.preventDefault){
3071 this.el.on('click', this.onClick, this);
3075 onClick : function(e)
3077 if(this.preventDefault){
3080 //Roo.log('img onclick');
3081 this.fireEvent('click', this, e);
3094 * @class Roo.bootstrap.Header
3095 * @extends Roo.bootstrap.Component
3096 * Bootstrap Header class
3097 * @cfg {String} html content of header
3098 * @cfg {Number} level (1|2|3|4|5|6) default 1
3101 * Create a new Header
3102 * @param {Object} config The config object
3106 Roo.bootstrap.Header = function(config){
3107 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3118 getAutoCreate : function(){
3123 tag: 'h' + (1 *this.level),
3124 html: this.html || ''
3136 * Ext JS Library 1.1.1
3137 * Copyright(c) 2006-2007, Ext JS, LLC.
3139 * Originally Released Under LGPL - original licence link has changed is not relivant.
3142 * <script type="text/javascript">
3146 * @class Roo.bootstrap.MenuMgr
3147 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3150 Roo.bootstrap.MenuMgr = function(){
3151 var menus, active, groups = {}, attached = false, lastShow = new Date();
3153 // private - called when first menu is created
3156 active = new Roo.util.MixedCollection();
3157 Roo.get(document).addKeyListener(27, function(){
3158 if(active.length > 0){
3166 if(active && active.length > 0){
3167 var c = active.clone();
3177 if(active.length < 1){
3178 Roo.get(document).un("mouseup", onMouseDown);
3186 var last = active.last();
3187 lastShow = new Date();
3190 Roo.get(document).on("mouseup", onMouseDown);
3195 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196 m.parentMenu.activeChild = m;
3197 }else if(last && last.isVisible()){
3198 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3203 function onBeforeHide(m){
3205 m.activeChild.hide();
3207 if(m.autoHideTimer){
3208 clearTimeout(m.autoHideTimer);
3209 delete m.autoHideTimer;
3214 function onBeforeShow(m){
3215 var pm = m.parentMenu;
3216 if(!pm && !m.allowOtherMenus){
3218 }else if(pm && pm.activeChild && active != m){
3219 pm.activeChild.hide();
3223 // private this should really trigger on mouseup..
3224 function onMouseDown(e){
3225 Roo.log("on Mouse Up");
3227 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228 Roo.log("MenuManager hideAll");
3237 function onBeforeCheck(mi, state){
3239 var g = groups[mi.group];
3240 for(var i = 0, l = g.length; i < l; i++){
3242 g[i].setChecked(false);
3251 * Hides all menus that are currently visible
3253 hideAll : function(){
3258 register : function(menu){
3262 menus[menu.id] = menu;
3263 menu.on("beforehide", onBeforeHide);
3264 menu.on("hide", onHide);
3265 menu.on("beforeshow", onBeforeShow);
3266 menu.on("show", onShow);
3268 if(g && menu.events["checkchange"]){
3272 groups[g].push(menu);
3273 menu.on("checkchange", onCheck);
3278 * Returns a {@link Roo.menu.Menu} object
3279 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280 * be used to generate and return a new Menu instance.
3282 get : function(menu){
3283 if(typeof menu == "string"){ // menu id
3285 }else if(menu.events){ // menu instance
3288 /*else if(typeof menu.length == 'number'){ // array of menu items?
3289 return new Roo.bootstrap.Menu({items:menu});
3290 }else{ // otherwise, must be a config
3291 return new Roo.bootstrap.Menu(menu);
3298 unregister : function(menu){
3299 delete menus[menu.id];
3300 menu.un("beforehide", onBeforeHide);
3301 menu.un("hide", onHide);
3302 menu.un("beforeshow", onBeforeShow);
3303 menu.un("show", onShow);
3305 if(g && menu.events["checkchange"]){
3306 groups[g].remove(menu);
3307 menu.un("checkchange", onCheck);
3312 registerCheckable : function(menuItem){
3313 var g = menuItem.group;
3318 groups[g].push(menuItem);
3319 menuItem.on("beforecheckchange", onBeforeCheck);
3324 unregisterCheckable : function(menuItem){
3325 var g = menuItem.group;
3327 groups[g].remove(menuItem);
3328 menuItem.un("beforecheckchange", onBeforeCheck);
3340 * @class Roo.bootstrap.Menu
3341 * @extends Roo.bootstrap.Component
3342 * Bootstrap Menu class - container for MenuItems
3343 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344 * @cfg {bool} hidden if the menu should be hidden when rendered.
3345 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3346 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3350 * @param {Object} config The config object
3354 Roo.bootstrap.Menu = function(config){
3355 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356 if (this.registerMenu && this.type != 'treeview') {
3357 Roo.bootstrap.MenuMgr.register(this);
3364 * Fires before this menu is displayed (return false to block)
3365 * @param {Roo.menu.Menu} this
3370 * Fires before this menu is hidden (return false to block)
3371 * @param {Roo.menu.Menu} this
3376 * Fires after this menu is displayed
3377 * @param {Roo.menu.Menu} this
3382 * Fires after this menu is hidden
3383 * @param {Roo.menu.Menu} this
3388 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389 * @param {Roo.menu.Menu} this
3390 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391 * @param {Roo.EventObject} e
3396 * Fires when the mouse is hovering over this menu
3397 * @param {Roo.menu.Menu} this
3398 * @param {Roo.EventObject} e
3399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3404 * Fires when the mouse exits this menu
3405 * @param {Roo.menu.Menu} this
3406 * @param {Roo.EventObject} e
3407 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3412 * Fires when a menu item contained in this menu is clicked
3413 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414 * @param {Roo.EventObject} e
3418 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3425 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3428 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3430 registerMenu : true,
3432 menuItems :false, // stores the menu items..
3442 getChildContainer : function() {
3446 getAutoCreate : function(){
3448 //if (['right'].indexOf(this.align)!==-1) {
3449 // cfg.cn[1].cls += ' pull-right'
3455 cls : 'dropdown-menu' ,
3456 style : 'z-index:1000'
3460 if (this.type === 'submenu') {
3461 cfg.cls = 'submenu active';
3463 if (this.type === 'treeview') {
3464 cfg.cls = 'treeview-menu';
3469 initEvents : function() {
3471 // Roo.log("ADD event");
3472 // Roo.log(this.triggerEl.dom);
3474 this.triggerEl.on('click', this.onTriggerClick, this);
3476 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3479 if (this.triggerEl.hasClass('nav-item')) {
3480 // dropdown toggle on the 'a' in BS4?
3481 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3483 this.triggerEl.addClass('dropdown-toggle');
3486 this.el.on('touchstart' , this.onTouch, this);
3488 this.el.on('click' , this.onClick, this);
3490 this.el.on("mouseover", this.onMouseOver, this);
3491 this.el.on("mouseout", this.onMouseOut, this);
3495 findTargetItem : function(e)
3497 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3501 //Roo.log(t); Roo.log(t.id);
3503 //Roo.log(this.menuitems);
3504 return this.menuitems.get(t.id);
3506 //return this.items.get(t.menuItemId);
3512 onTouch : function(e)
3514 Roo.log("menu.onTouch");
3515 //e.stopEvent(); this make the user popdown broken
3519 onClick : function(e)
3521 Roo.log("menu.onClick");
3523 var t = this.findTargetItem(e);
3524 if(!t || t.isContainer){
3529 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3530 if(t == this.activeItem && t.shouldDeactivate(e)){
3531 this.activeItem.deactivate();
3532 delete this.activeItem;
3536 this.setActiveItem(t, true);
3544 Roo.log('pass click event');
3548 this.fireEvent("click", this, t, e);
3552 if(!t.href.length || t.href == '#'){
3553 (function() { _this.hide(); }).defer(100);
3558 onMouseOver : function(e){
3559 var t = this.findTargetItem(e);
3562 // if(t.canActivate && !t.disabled){
3563 // this.setActiveItem(t, true);
3567 this.fireEvent("mouseover", this, e, t);
3569 isVisible : function(){
3570 return !this.hidden;
3572 onMouseOut : function(e){
3573 var t = this.findTargetItem(e);
3576 // if(t == this.activeItem && t.shouldDeactivate(e)){
3577 // this.activeItem.deactivate();
3578 // delete this.activeItem;
3581 this.fireEvent("mouseout", this, e, t);
3586 * Displays this menu relative to another element
3587 * @param {String/HTMLElement/Roo.Element} element The element to align to
3588 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589 * the element (defaults to this.defaultAlign)
3590 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3592 show : function(el, pos, parentMenu)
3594 if (false === this.fireEvent("beforeshow", this)) {
3595 Roo.log("show canceled");
3598 this.parentMenu = parentMenu;
3603 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3606 * Displays this menu at a specific xy position
3607 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3610 showAt : function(xy, parentMenu, /* private: */_e){
3611 this.parentMenu = parentMenu;
3616 this.fireEvent("beforeshow", this);
3617 //xy = this.el.adjustForConstraints(xy);
3621 this.hideMenuItems();
3622 this.hidden = false;
3623 this.triggerEl.addClass('open');
3624 this.el.addClass('show');
3626 // reassign x when hitting right
3627 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3631 // reassign y when hitting bottom
3632 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3636 // but the list may align on trigger left or trigger top... should it be a properity?
3638 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3643 this.fireEvent("show", this);
3649 this.doFocus.defer(50, this);
3653 doFocus : function(){
3655 this.focusEl.focus();
3660 * Hides this menu and optionally all parent menus
3661 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3663 hide : function(deep)
3665 if (false === this.fireEvent("beforehide", this)) {
3666 Roo.log("hide canceled");
3669 this.hideMenuItems();
3670 if(this.el && this.isVisible()){
3672 if(this.activeItem){
3673 this.activeItem.deactivate();
3674 this.activeItem = null;
3676 this.triggerEl.removeClass('open');;
3677 this.el.removeClass('show');
3679 this.fireEvent("hide", this);
3681 if(deep === true && this.parentMenu){
3682 this.parentMenu.hide(true);
3686 onTriggerClick : function(e)
3688 Roo.log('trigger click');
3690 var target = e.getTarget();
3692 Roo.log(target.nodeName.toLowerCase());
3694 if(target.nodeName.toLowerCase() === 'i'){
3700 onTriggerPress : function(e)
3702 Roo.log('trigger press');
3703 //Roo.log(e.getTarget());
3704 // Roo.log(this.triggerEl.dom);
3706 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707 var pel = Roo.get(e.getTarget());
3708 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709 Roo.log('is treeview or dropdown?');
3713 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3717 if (this.isVisible()) {
3722 this.show(this.triggerEl, '?', false);
3725 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3732 hideMenuItems : function()
3734 Roo.log("hide Menu Items");
3739 this.el.select('.open',true).each(function(aa) {
3741 aa.removeClass('open');
3745 addxtypeChild : function (tree, cntr) {
3746 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3748 this.menuitems.add(comp);
3760 this.getEl().dom.innerHTML = '';
3761 this.menuitems.clear();
3775 * @class Roo.bootstrap.MenuItem
3776 * @extends Roo.bootstrap.Component
3777 * Bootstrap MenuItem class
3778 * @cfg {String} html the menu label
3779 * @cfg {String} href the link
3780 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782 * @cfg {Boolean} active used on sidebars to highlight active itesm
3783 * @cfg {String} fa favicon to show on left of menu item.
3784 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3788 * Create a new MenuItem
3789 * @param {Object} config The config object
3793 Roo.bootstrap.MenuItem = function(config){
3794 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3799 * The raw click event for the entire grid.
3800 * @param {Roo.bootstrap.MenuItem} this
3801 * @param {Roo.EventObject} e
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3811 preventDefault: false,
3812 isContainer : false,
3816 getAutoCreate : function(){
3818 if(this.isContainer){
3821 cls: 'dropdown-menu-item '
3831 cls : 'dropdown-item',
3836 if (this.fa !== false) {
3839 cls : 'fa fa-' + this.fa
3848 cls: 'dropdown-menu-item',
3851 if (this.parent().type == 'treeview') {
3852 cfg.cls = 'treeview-menu';
3855 cfg.cls += ' active';
3860 anc.href = this.href || cfg.cn[0].href ;
3861 ctag.html = this.html || cfg.cn[0].html ;
3865 initEvents: function()
3867 if (this.parent().type == 'treeview') {
3868 this.el.select('a').on('click', this.onClick, this);
3872 this.menu.parentType = this.xtype;
3873 this.menu.triggerEl = this.el;
3874 this.menu = this.addxtype(Roo.apply({}, this.menu));
3878 onClick : function(e)
3880 Roo.log('item on click ');
3882 if(this.preventDefault){
3885 //this.parent().hideMenuItems();
3887 this.fireEvent('click', this, e);
3906 * @class Roo.bootstrap.MenuSeparator
3907 * @extends Roo.bootstrap.Component
3908 * Bootstrap MenuSeparator class
3911 * Create a new MenuItem
3912 * @param {Object} config The config object
3916 Roo.bootstrap.MenuSeparator = function(config){
3917 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3922 getAutoCreate : function(){
3941 * @class Roo.bootstrap.Modal
3942 * @extends Roo.bootstrap.Component
3943 * Bootstrap Modal class
3944 * @cfg {String} title Title of dialog
3945 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3947 * @cfg {Boolean} specificTitle default false
3948 * @cfg {Array} buttons Array of buttons or standard button set..
3949 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950 * @cfg {Boolean} animate default true
3951 * @cfg {Boolean} allow_close default true
3952 * @cfg {Boolean} fitwindow default false
3953 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956 * @cfg {String} size (sm|lg|xl) default empty
3957 * @cfg {Number} max_width set the max width of modal
3958 * @cfg {Boolean} editableTitle can the title be edited
3963 * Create a new Modal Dialog
3964 * @param {Object} config The config object
3967 Roo.bootstrap.Modal = function(config){
3968 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3973 * The raw btnclick event for the button
3974 * @param {Roo.EventObject} e
3979 * Fire when dialog resize
3980 * @param {Roo.bootstrap.Modal} this
3981 * @param {Roo.EventObject} e
3985 * @event titlechanged
3986 * Fire when the editable title has been changed
3987 * @param {Roo.bootstrap.Modal} this
3988 * @param {Roo.EventObject} value
3990 "titlechanged" : true
3993 this.buttons = this.buttons || [];
3996 this.tmpl = Roo.factory(this.tmpl);
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4003 title : 'test dialog',
4013 specificTitle: false,
4015 buttonPosition: 'right',
4037 editableTitle : false,
4039 onRender : function(ct, position)
4041 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4044 var cfg = Roo.apply({}, this.getAutoCreate());
4047 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4049 //if (!cfg.name.length) {
4053 cfg.cls += ' ' + this.cls;
4056 cfg.style = this.style;
4058 this.el = Roo.get(document.body).createChild(cfg, position);
4060 //var type = this.el.dom.type;
4063 if(this.tabIndex !== undefined){
4064 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4067 this.dialogEl = this.el.select('.modal-dialog',true).first();
4068 this.bodyEl = this.el.select('.modal-body',true).first();
4069 this.closeEl = this.el.select('.modal-header .close', true).first();
4070 this.headerEl = this.el.select('.modal-header',true).first();
4071 this.titleEl = this.el.select('.modal-title',true).first();
4072 this.footerEl = this.el.select('.modal-footer',true).first();
4074 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4076 //this.el.addClass("x-dlg-modal");
4078 if (this.buttons.length) {
4079 Roo.each(this.buttons, function(bb) {
4080 var b = Roo.apply({}, bb);
4081 b.xns = b.xns || Roo.bootstrap;
4082 b.xtype = b.xtype || 'Button';
4083 if (typeof(b.listeners) == 'undefined') {
4084 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4087 var btn = Roo.factory(b);
4089 btn.render(this.getButtonContainer());
4093 // render the children.
4096 if(typeof(this.items) != 'undefined'){
4097 var items = this.items;
4100 for(var i =0;i < items.length;i++) {
4101 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4105 this.items = nitems;
4107 // where are these used - they used to be body/close/footer
4111 //this.el.addClass([this.fieldClass, this.cls]);
4115 getAutoCreate : function()
4117 // we will default to modal-body-overflow - might need to remove or make optional later.
4119 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4120 html : this.html || ''
4125 cls : 'modal-title',
4129 if(this.specificTitle){ // WTF is this?
4134 if (this.allow_close && Roo.bootstrap.version == 3) {
4144 if (this.editableTitle) {
4146 cls: 'form-control roo-editable-title d-none',
4152 if (this.allow_close && Roo.bootstrap.version == 4) {
4162 if(this.size.length){
4163 size = 'modal-' + this.size;
4166 var footer = Roo.bootstrap.version == 3 ?
4168 cls : 'modal-footer',
4172 cls: 'btn-' + this.buttonPosition
4177 { // BS4 uses mr-auto on left buttons....
4178 cls : 'modal-footer'
4189 cls: "modal-dialog " + size,
4192 cls : "modal-content",
4195 cls : 'modal-header',
4210 modal.cls += ' fade';
4216 getChildContainer : function() {
4221 getButtonContainer : function() {
4223 return Roo.bootstrap.version == 4 ?
4224 this.el.select('.modal-footer',true).first()
4225 : this.el.select('.modal-footer div',true).first();
4228 initEvents : function()
4230 if (this.allow_close) {
4231 this.closeEl.on('click', this.hide, this);
4233 Roo.EventManager.onWindowResize(this.resize, this, true);
4234 if (this.editableTitle) {
4235 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4236 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237 this.headerEditEl.on('keyup', function(e) {
4238 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239 this.toggleHeaderInput(false)
4242 this.headerEditEl.on('blur', function(e) {
4243 this.toggleHeaderInput(false)
4252 this.maskEl.setSize(
4253 Roo.lib.Dom.getViewWidth(true),
4254 Roo.lib.Dom.getViewHeight(true)
4257 if (this.fitwindow) {
4259 this.dialogEl.setStyle( { 'max-width' : '100%' });
4261 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4267 if(this.max_width !== 0) {
4269 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4272 this.setSize(w, this.height);
4276 if(this.max_height) {
4277 this.setSize(w,Math.min(
4279 Roo.lib.Dom.getViewportHeight(true) - 60
4285 if(!this.fit_content) {
4286 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4290 this.setSize(w, Math.min(
4292 this.headerEl.getHeight() +
4293 this.footerEl.getHeight() +
4294 this.getChildHeight(this.bodyEl.dom.childNodes),
4295 Roo.lib.Dom.getViewportHeight(true) - 60)
4301 setSize : function(w,h)
4312 if (!this.rendered) {
4315 this.toggleHeaderInput(false);
4316 //this.el.setStyle('display', 'block');
4317 this.el.removeClass('hideing');
4318 this.el.dom.style.display='block';
4320 Roo.get(document.body).addClass('modal-open');
4322 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4325 this.el.addClass('show');
4326 this.el.addClass('in');
4329 this.el.addClass('show');
4330 this.el.addClass('in');
4333 // not sure how we can show data in here..
4335 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4338 Roo.get(document.body).addClass("x-body-masked");
4340 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4341 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342 this.maskEl.dom.style.display = 'block';
4343 this.maskEl.addClass('show');
4348 this.fireEvent('show', this);
4350 // set zindex here - otherwise it appears to be ignored...
4351 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4354 this.items.forEach( function(e) {
4355 e.layout ? e.layout() : false;
4363 if(this.fireEvent("beforehide", this) !== false){
4365 this.maskEl.removeClass('show');
4367 this.maskEl.dom.style.display = '';
4368 Roo.get(document.body).removeClass("x-body-masked");
4369 this.el.removeClass('in');
4370 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4372 if(this.animate){ // why
4373 this.el.addClass('hideing');
4374 this.el.removeClass('show');
4376 if (!this.el.hasClass('hideing')) {
4377 return; // it's been shown again...
4380 this.el.dom.style.display='';
4382 Roo.get(document.body).removeClass('modal-open');
4383 this.el.removeClass('hideing');
4387 this.el.removeClass('show');
4388 this.el.dom.style.display='';
4389 Roo.get(document.body).removeClass('modal-open');
4392 this.fireEvent('hide', this);
4395 isVisible : function()
4398 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4402 addButton : function(str, cb)
4406 var b = Roo.apply({}, { html : str } );
4407 b.xns = b.xns || Roo.bootstrap;
4408 b.xtype = b.xtype || 'Button';
4409 if (typeof(b.listeners) == 'undefined') {
4410 b.listeners = { click : cb.createDelegate(this) };
4413 var btn = Roo.factory(b);
4415 btn.render(this.getButtonContainer());
4421 setDefaultButton : function(btn)
4423 //this.el.select('.modal-footer').()
4426 resizeTo: function(w,h)
4428 this.dialogEl.setWidth(w);
4430 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4432 this.bodyEl.setHeight(h - diff);
4434 this.fireEvent('resize', this);
4437 setContentSize : function(w, h)
4441 onButtonClick: function(btn,e)
4444 this.fireEvent('btnclick', btn.name, e);
4447 * Set the title of the Dialog
4448 * @param {String} str new Title
4450 setTitle: function(str) {
4451 this.titleEl.dom.innerHTML = str;
4455 * Set the body of the Dialog
4456 * @param {String} str new Title
4458 setBody: function(str) {
4459 this.bodyEl.dom.innerHTML = str;
4462 * Set the body of the Dialog using the template
4463 * @param {Obj} data - apply this data to the template and replace the body contents.
4465 applyBody: function(obj)
4468 Roo.log("Error - using apply Body without a template");
4471 this.tmpl.overwrite(this.bodyEl, obj);
4474 getChildHeight : function(child_nodes)
4478 child_nodes.length == 0
4483 var child_height = 0;
4485 for(var i = 0; i < child_nodes.length; i++) {
4488 * for modal with tabs...
4489 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4491 var layout_childs = child_nodes[i].childNodes;
4493 for(var j = 0; j < layout_childs.length; j++) {
4495 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4497 var layout_body_childs = layout_childs[j].childNodes;
4499 for(var k = 0; k < layout_body_childs.length; k++) {
4501 if(layout_body_childs[k].classList.contains('navbar')) {
4502 child_height += layout_body_childs[k].offsetHeight;
4506 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4508 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4510 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4512 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4528 child_height += child_nodes[i].offsetHeight;
4529 // Roo.log(child_nodes[i].offsetHeight);
4532 return child_height;
4534 toggleHeaderInput : function(is_edit)
4536 if (!this.editableTitle) {
4537 return; // not editable.
4539 if (is_edit && this.is_header_editing) {
4540 return; // already editing..
4544 this.headerEditEl.dom.value = this.title;
4545 this.headerEditEl.removeClass('d-none');
4546 this.headerEditEl.dom.focus();
4547 this.titleEl.addClass('d-none');
4549 this.is_header_editing = true;
4552 // flip back to not editing.
4553 this.title = this.headerEditEl.dom.value;
4554 this.headerEditEl.addClass('d-none');
4555 this.titleEl.removeClass('d-none');
4556 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557 this.is_header_editing = false;
4558 this.fireEvent('titlechanged', this, this.title);
4567 Roo.apply(Roo.bootstrap.Modal, {
4569 * Button config that displays a single OK button
4578 * Button config that displays Yes and No buttons
4594 * Button config that displays OK and Cancel buttons
4609 * Button config that displays Yes, No and Cancel buttons
4634 * messagebox - can be used as a replace
4638 * @class Roo.MessageBox
4639 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4648 // process text value...
4652 // Show a dialog using config options:
4654 title:'Save Changes?',
4655 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656 buttons: Roo.Msg.YESNOCANCEL,
4663 Roo.bootstrap.MessageBox = function(){
4664 var dlg, opt, mask, waitTimer;
4665 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666 var buttons, activeTextEl, bwidth;
4670 var handleButton = function(button){
4672 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4676 var handleHide = function(){
4678 dlg.el.removeClass(opt.cls);
4681 // Roo.TaskMgr.stop(waitTimer);
4682 // waitTimer = null;
4687 var updateButtons = function(b){
4690 buttons["ok"].hide();
4691 buttons["cancel"].hide();
4692 buttons["yes"].hide();
4693 buttons["no"].hide();
4694 dlg.footerEl.hide();
4698 dlg.footerEl.show();
4699 for(var k in buttons){
4700 if(typeof buttons[k] != "function"){
4703 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704 width += buttons[k].el.getWidth()+15;
4714 var handleEsc = function(d, k, e){
4715 if(opt && opt.closable !== false){
4725 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726 * @return {Roo.BasicDialog} The BasicDialog element
4728 getDialog : function(){
4730 dlg = new Roo.bootstrap.Modal( {
4733 //constraintoviewport:false,
4735 //collapsible : false,
4740 //buttonAlign:"center",
4741 closeClick : function(){
4742 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4745 handleButton("cancel");
4750 dlg.on("hide", handleHide);
4752 //dlg.addKeyListener(27, handleEsc);
4754 this.buttons = buttons;
4755 var bt = this.buttonText;
4756 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4761 bodyEl = dlg.bodyEl.createChild({
4763 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764 '<textarea class="roo-mb-textarea"></textarea>' +
4765 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4767 msgEl = bodyEl.dom.firstChild;
4768 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769 textboxEl.enableDisplayMode();
4770 textboxEl.addKeyListener([10,13], function(){
4771 if(dlg.isVisible() && opt && opt.buttons){
4774 }else if(opt.buttons.yes){
4775 handleButton("yes");
4779 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780 textareaEl.enableDisplayMode();
4781 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782 progressEl.enableDisplayMode();
4784 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785 var pf = progressEl.dom.firstChild;
4787 pp = Roo.get(pf.firstChild);
4788 pp.setHeight(pf.offsetHeight);
4796 * Updates the message box body text
4797 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798 * the XHTML-compliant non-breaking space character '&#160;')
4799 * @return {Roo.MessageBox} This message box
4801 updateText : function(text)
4803 if(!dlg.isVisible() && !opt.width){
4804 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4807 msgEl.innerHTML = text || ' ';
4809 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4812 Math.min(opt.width || cw , this.maxWidth),
4813 Math.max(opt.minWidth || this.minWidth, bwidth)
4816 activeTextEl.setWidth(w);
4818 if(dlg.isVisible()){
4819 dlg.fixedcenter = false;
4821 // to big, make it scroll. = But as usual stupid IE does not support
4824 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4828 bodyEl.dom.style.height = '';
4829 bodyEl.dom.style.overflowY = '';
4832 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4834 bodyEl.dom.style.overflowX = '';
4837 dlg.setContentSize(w, bodyEl.getHeight());
4838 if(dlg.isVisible()){
4839 dlg.fixedcenter = true;
4845 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4846 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849 * @return {Roo.MessageBox} This message box
4851 updateProgress : function(value, text){
4853 this.updateText(text);
4856 if (pp) { // weird bug on my firefox - for some reason this is not defined
4857 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4864 * Returns true if the message box is currently displayed
4865 * @return {Boolean} True if the message box is visible, else false
4867 isVisible : function(){
4868 return dlg && dlg.isVisible();
4872 * Hides the message box if it is displayed
4875 if(this.isVisible()){
4881 * Displays a new message box, or reinitializes an existing message box, based on the config options
4882 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883 * The following config object properties are supported:
4885 Property Type Description
4886 ---------- --------------- ------------------------------------------------------------------------------------
4887 animEl String/Element An id or Element from which the message box should animate as it opens and
4888 closes (defaults to undefined)
4889 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable Boolean False to hide the top-right close button (defaults to true). Note that
4892 progress and wait dialogs will ignore this property and always hide the
4893 close button as they can only be closed programmatically.
4894 cls String A custom CSS class to apply to the message box element
4895 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4896 displayed (defaults to 75)
4897 fn Function A callback function to execute after closing the dialog. The arguments to the
4898 function will be btn (the name of the button that was clicked, if applicable,
4899 e.g. "ok"), and text (the value of the active text field, if applicable).
4900 Progress and wait dialogs will ignore this option since they do not respond to
4901 user actions and can only be closed programmatically, so any required function
4902 should be called by the same code after it closes the dialog.
4903 icon String A CSS class that provides a background image to be used as an icon for
4904 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4906 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4907 modal Boolean False to allow user interaction with the page while the message box is
4908 displayed (defaults to true)
4909 msg String A string that will replace the existing message box body text (defaults
4910 to the XHTML-compliant non-breaking space character ' ')
4911 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4912 progress Boolean True to display a progress bar (defaults to false)
4913 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4916 title String The title text
4917 value String The string value to set into the active textbox element if displayed
4918 wait Boolean True to display a progress bar (defaults to false)
4919 width Number The width of the dialog in pixels
4926 msg: 'Please enter your address:',
4928 buttons: Roo.MessageBox.OKCANCEL,
4931 animEl: 'addAddressBtn'
4934 * @param {Object} config Configuration options
4935 * @return {Roo.MessageBox} This message box
4937 show : function(options)
4940 // this causes nightmares if you show one dialog after another
4941 // especially on callbacks..
4943 if(this.isVisible()){
4946 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4948 Roo.log("New Dialog Message:" + options.msg )
4949 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4953 var d = this.getDialog();
4955 d.setTitle(opt.title || " ");
4956 d.closeEl.setDisplayed(opt.closable !== false);
4957 activeTextEl = textboxEl;
4958 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4963 textareaEl.setHeight(typeof opt.multiline == "number" ?
4964 opt.multiline : this.defaultTextHeight);
4965 activeTextEl = textareaEl;
4974 progressEl.setDisplayed(opt.progress === true);
4976 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4978 this.updateProgress(0);
4979 activeTextEl.dom.value = opt.value || "";
4981 dlg.setDefaultButton(activeTextEl);
4983 var bs = opt.buttons;
4987 }else if(bs && bs.yes){
4988 db = buttons["yes"];
4990 dlg.setDefaultButton(db);
4992 bwidth = updateButtons(opt.buttons);
4993 this.updateText(opt.msg);
4995 d.el.addClass(opt.cls);
4997 d.proxyDrag = opt.proxyDrag === true;
4998 d.modal = opt.modal !== false;
4999 d.mask = opt.modal !== false ? mask : false;
5001 // force it to the end of the z-index stack so it gets a cursor in FF
5002 document.body.appendChild(dlg.el.dom);
5003 d.animateTarget = null;
5004 d.show(options.animEl);
5010 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5011 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012 * and closing the message box when the process is complete.
5013 * @param {String} title The title bar text
5014 * @param {String} msg The message box body text
5015 * @return {Roo.MessageBox} This message box
5017 progress : function(title, msg){
5024 minWidth: this.minProgressWidth,
5031 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032 * If a callback function is passed it will be called after the user clicks the button, and the
5033 * id of the button that was clicked will be passed as the only parameter to the callback
5034 * (could also be the top-right close button).
5035 * @param {String} title The title bar text
5036 * @param {String} msg The message box body text
5037 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038 * @param {Object} scope (optional) The scope of the callback function
5039 * @return {Roo.MessageBox} This message box
5041 alert : function(title, msg, fn, scope)
5056 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5057 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058 * You are responsible for closing the message box when the process is complete.
5059 * @param {String} msg The message box body text
5060 * @param {String} title (optional) The title bar text
5061 * @return {Roo.MessageBox} This message box
5063 wait : function(msg, title){
5074 waitTimer = Roo.TaskMgr.start({
5076 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5084 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087 * @param {String} title The title bar text
5088 * @param {String} msg The message box body text
5089 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090 * @param {Object} scope (optional) The scope of the callback function
5091 * @return {Roo.MessageBox} This message box
5093 confirm : function(title, msg, fn, scope){
5097 buttons: this.YESNO,
5106 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5108 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109 * (could also be the top-right close button) and the text that was entered will be passed as the two
5110 * parameters to the callback.
5111 * @param {String} title The title bar text
5112 * @param {String} msg The message box body text
5113 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114 * @param {Object} scope (optional) The scope of the callback function
5115 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117 * @return {Roo.MessageBox} This message box
5119 prompt : function(title, msg, fn, scope, multiline){
5123 buttons: this.OKCANCEL,
5128 multiline: multiline,
5135 * Button config that displays a single OK button
5140 * Button config that displays Yes and No buttons
5143 YESNO : {yes:true, no:true},
5145 * Button config that displays OK and Cancel buttons
5148 OKCANCEL : {ok:true, cancel:true},
5150 * Button config that displays Yes, No and Cancel buttons
5153 YESNOCANCEL : {yes:true, no:true, cancel:true},
5156 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5159 defaultTextHeight : 75,
5161 * The maximum width in pixels of the message box (defaults to 600)
5166 * The minimum width in pixels of the message box (defaults to 100)
5171 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5172 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5175 minProgressWidth : 250,
5177 * An object containing the default button text strings that can be overriden for localized language support.
5178 * Supported properties are: ok, cancel, yes and no.
5179 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5192 * Shorthand for {@link Roo.MessageBox}
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5204 * @class Roo.bootstrap.Navbar
5205 * @extends Roo.bootstrap.Component
5206 * Bootstrap Navbar class
5209 * Create a new Navbar
5210 * @param {Object} config The config object
5214 Roo.bootstrap.Navbar = function(config){
5215 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5219 * @event beforetoggle
5220 * Fire before toggle the menu
5221 * @param {Roo.EventObject} e
5223 "beforetoggle" : true
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5236 getAutoCreate : function(){
5239 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5243 initEvents :function ()
5245 //Roo.log(this.el.select('.navbar-toggle',true));
5246 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5253 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5255 var size = this.el.getSize();
5256 this.maskEl.setSize(size.width, size.height);
5257 this.maskEl.enableDisplayMode("block");
5266 getChildContainer : function()
5268 if (this.el && this.el.select('.collapse').getCount()) {
5269 return this.el.select('.collapse',true).first();
5284 onToggle : function()
5287 if(this.fireEvent('beforetoggle', this) === false){
5290 var ce = this.el.select('.navbar-collapse',true).first();
5292 if (!ce.hasClass('show')) {
5302 * Expand the navbar pulldown
5304 expand : function ()
5307 var ce = this.el.select('.navbar-collapse',true).first();
5308 if (ce.hasClass('collapsing')) {
5311 ce.dom.style.height = '';
5313 ce.addClass('in'); // old...
5314 ce.removeClass('collapse');
5315 ce.addClass('show');
5316 var h = ce.getHeight();
5318 ce.removeClass('show');
5319 // at this point we should be able to see it..
5320 ce.addClass('collapsing');
5322 ce.setHeight(0); // resize it ...
5323 ce.on('transitionend', function() {
5324 //Roo.log('done transition');
5325 ce.removeClass('collapsing');
5326 ce.addClass('show');
5327 ce.removeClass('collapse');
5329 ce.dom.style.height = '';
5330 }, this, { single: true} );
5332 ce.dom.scrollTop = 0;
5335 * Collapse the navbar pulldown
5337 collapse : function()
5339 var ce = this.el.select('.navbar-collapse',true).first();
5341 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342 // it's collapsed or collapsing..
5345 ce.removeClass('in'); // old...
5346 ce.setHeight(ce.getHeight());
5347 ce.removeClass('show');
5348 ce.addClass('collapsing');
5350 ce.on('transitionend', function() {
5351 ce.dom.style.height = '';
5352 ce.removeClass('collapsing');
5353 ce.addClass('collapse');
5354 }, this, { single: true} );
5374 * @class Roo.bootstrap.NavSimplebar
5375 * @extends Roo.bootstrap.Navbar
5376 * Bootstrap Sidebar class
5378 * @cfg {Boolean} inverse is inverted color
5380 * @cfg {String} type (nav | pills | tabs)
5381 * @cfg {Boolean} arrangement stacked | justified
5382 * @cfg {String} align (left | right) alignment
5384 * @cfg {Boolean} main (true|false) main nav bar? default false
5385 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5387 * @cfg {String} tag (header|footer|nav|div) default is nav
5389 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5393 * Create a new Sidebar
5394 * @param {Object} config The config object
5398 Roo.bootstrap.NavSimplebar = function(config){
5399 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5418 getAutoCreate : function(){
5422 tag : this.tag || 'div',
5423 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5425 if (['light','white'].indexOf(this.weight) > -1) {
5426 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5428 cfg.cls += ' bg-' + this.weight;
5431 cfg.cls += ' navbar-inverse';
5435 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5437 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5446 cls: 'nav nav-' + this.xtype,
5452 this.type = this.type || 'nav';
5453 if (['tabs','pills'].indexOf(this.type) != -1) {
5454 cfg.cn[0].cls += ' nav-' + this.type
5458 if (this.type!=='nav') {
5459 Roo.log('nav type must be nav/tabs/pills')
5461 cfg.cn[0].cls += ' navbar-nav'
5467 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468 cfg.cn[0].cls += ' nav-' + this.arrangement;
5472 if (this.align === 'right') {
5473 cfg.cn[0].cls += ' navbar-right';
5498 * navbar-expand-md fixed-top
5502 * @class Roo.bootstrap.NavHeaderbar
5503 * @extends Roo.bootstrap.NavSimplebar
5504 * Bootstrap Sidebar class
5506 * @cfg {String} brand what is brand
5507 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508 * @cfg {String} brand_href href of the brand
5509 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5510 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5515 * Create a new Sidebar
5516 * @param {Object} config The config object
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5532 desktopCenter : false,
5535 getAutoCreate : function(){
5538 tag: this.nav || 'nav',
5539 cls: 'navbar navbar-expand-md',
5545 if (this.desktopCenter) {
5546 cn.push({cls : 'container', cn : []});
5554 cls: 'navbar-toggle navbar-toggler',
5555 'data-toggle': 'collapse',
5560 html: 'Toggle navigation'
5564 cls: 'icon-bar navbar-toggler-icon'
5577 cn.push( Roo.bootstrap.version == 4 ? btn : {
5579 cls: 'navbar-header',
5588 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5592 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5594 if (['light','white'].indexOf(this.weight) > -1) {
5595 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5597 cfg.cls += ' bg-' + this.weight;
5600 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5603 // tag can override this..
5605 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5608 if (this.brand !== '') {
5609 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5612 href: this.brand_href ? this.brand_href : '#',
5613 cls: 'navbar-brand',
5621 cfg.cls += ' main-nav';
5629 getHeaderChildContainer : function()
5631 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632 return this.el.select('.navbar-header',true).first();
5635 return this.getChildContainer();
5638 getChildContainer : function()
5641 return this.el.select('.roo-navbar-collapse',true).first();
5646 initEvents : function()
5648 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5650 if (this.autohide) {
5655 Roo.get(document).on('scroll',function(e) {
5656 var ns = Roo.get(document).getScroll().top;
5657 var os = prevScroll;
5661 ft.removeClass('slideDown');
5662 ft.addClass('slideUp');
5665 ft.removeClass('slideUp');
5666 ft.addClass('slideDown');
5687 * @class Roo.bootstrap.NavSidebar
5688 * @extends Roo.bootstrap.Navbar
5689 * Bootstrap Sidebar class
5692 * Create a new Sidebar
5693 * @param {Object} config The config object
5697 Roo.bootstrap.NavSidebar = function(config){
5698 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5703 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5705 getAutoCreate : function(){
5710 cls: 'sidebar sidebar-nav'
5732 * @class Roo.bootstrap.NavGroup
5733 * @extends Roo.bootstrap.Component
5734 * Bootstrap NavGroup class
5735 * @cfg {String} align (left|right)
5736 * @cfg {Boolean} inverse
5737 * @cfg {String} type (nav|pills|tab) default nav
5738 * @cfg {String} navId - reference Id for navbar.
5739 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5742 * Create a new nav group
5743 * @param {Object} config The config object
5746 Roo.bootstrap.NavGroup = function(config){
5747 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5750 Roo.bootstrap.NavGroup.register(this);
5754 * Fires when the active item changes
5755 * @param {Roo.bootstrap.NavGroup} this
5756 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5776 getAutoCreate : function()
5778 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5784 if (Roo.bootstrap.version == 4) {
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cls += ' nav-' + this.type;
5788 // trying to remove so header bar can right align top?
5789 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790 // do not use on header bar...
5791 cfg.cls += ' navbar-nav';
5796 if (['tabs','pills'].indexOf(this.type) != -1) {
5797 cfg.cls += ' nav-' + this.type
5799 if (this.type !== 'nav') {
5800 Roo.log('nav type must be nav/tabs/pills')
5802 cfg.cls += ' navbar-nav'
5806 if (this.parent() && this.parent().sidebar) {
5809 cls: 'dashboard-menu sidebar-menu'
5815 if (this.form === true) {
5818 cls: 'navbar-form form-inline'
5820 //nav navbar-right ml-md-auto
5821 if (this.align === 'right') {
5822 cfg.cls += ' navbar-right ml-md-auto';
5824 cfg.cls += ' navbar-left';
5828 if (this.align === 'right') {
5829 cfg.cls += ' navbar-right ml-md-auto';
5831 cfg.cls += ' mr-auto';
5835 cfg.cls += ' navbar-inverse';
5843 * sets the active Navigation item
5844 * @param {Roo.bootstrap.NavItem} the new current navitem
5846 setActiveItem : function(item)
5849 Roo.each(this.navItems, function(v){
5854 v.setActive(false, true);
5861 item.setActive(true, true);
5862 this.fireEvent('changed', this, item, prev);
5867 * gets the active Navigation item
5868 * @return {Roo.bootstrap.NavItem} the current navitem
5870 getActive : function()
5874 Roo.each(this.navItems, function(v){
5885 indexOfNav : function()
5889 Roo.each(this.navItems, function(v,i){
5900 * adds a Navigation item
5901 * @param {Roo.bootstrap.NavItem} the navitem to add
5903 addItem : function(cfg)
5905 if (this.form && Roo.bootstrap.version == 4) {
5908 var cn = new Roo.bootstrap.NavItem(cfg);
5910 cn.parentId = this.id;
5911 cn.onRender(this.el, null);
5915 * register a Navigation item
5916 * @param {Roo.bootstrap.NavItem} the navitem to add
5918 register : function(item)
5920 this.navItems.push( item);
5921 item.navId = this.navId;
5926 * clear all the Navigation item
5929 clearAll : function()
5932 this.el.dom.innerHTML = '';
5935 getNavItem: function(tabId)
5938 Roo.each(this.navItems, function(e) {
5939 if (e.tabId == tabId) {
5949 setActiveNext : function()
5951 var i = this.indexOfNav(this.getActive());
5952 if (i > this.navItems.length) {
5955 this.setActiveItem(this.navItems[i+1]);
5957 setActivePrev : function()
5959 var i = this.indexOfNav(this.getActive());
5963 this.setActiveItem(this.navItems[i-1]);
5965 clearWasActive : function(except) {
5966 Roo.each(this.navItems, function(e) {
5967 if (e.tabId != except.tabId && e.was_active) {
5968 e.was_active = false;
5975 getWasActive : function ()
5978 Roo.each(this.navItems, function(e) {
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5997 * register a Navigation Group
5998 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6000 register : function(navgrp)
6002 this.groups[navgrp.navId] = navgrp;
6006 * fetch a Navigation Group based on the navigation ID
6007 * @param {string} the navgroup to add
6008 * @returns {Roo.bootstrap.NavGroup} the navgroup
6010 get: function(navId) {
6011 if (typeof(this.groups[navId]) == 'undefined') {
6013 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6015 return this.groups[navId] ;
6030 * @class Roo.bootstrap.NavItem
6031 * @extends Roo.bootstrap.Component
6032 * Bootstrap Navbar.NavItem class
6033 * @cfg {String} href link to
6034 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035 * @cfg {Boolean} button_outline show and outlined button
6036 * @cfg {String} html content of button
6037 * @cfg {String} badge text inside badge
6038 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039 * @cfg {String} glyphicon DEPRICATED - use fa
6040 * @cfg {String} icon DEPRICATED - use fa
6041 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042 * @cfg {Boolean} active Is item active
6043 * @cfg {Boolean} disabled Is item disabled
6044 * @cfg {String} linkcls Link Class
6045 * @cfg {Boolean} preventDefault (true | false) default false
6046 * @cfg {String} tabId the tab that this item activates.
6047 * @cfg {String} tagtype (a|span) render as a href or span?
6048 * @cfg {Boolean} animateRef (true|false) link to element default false
6051 * Create a new Navbar Item
6052 * @param {Object} config The config object
6054 Roo.bootstrap.NavItem = function(config){
6055 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6060 * The raw click event for the entire grid.
6061 * @param {Roo.EventObject} e
6066 * Fires when the active item active state changes
6067 * @param {Roo.bootstrap.NavItem} this
6068 * @param {boolean} state the new state
6074 * Fires when scroll to element
6075 * @param {Roo.bootstrap.NavItem} this
6076 * @param {Object} options
6077 * @param {Roo.EventObject} e
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6094 preventDefault : false,
6102 button_outline : false,
6106 getAutoCreate : function(){
6113 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6116 cfg.cls += ' active' ;
6118 if (this.disabled) {
6119 cfg.cls += ' disabled';
6123 if (this.button_weight.length) {
6124 cfg.tag = this.href ? 'a' : 'button';
6125 cfg.html = this.html || '';
6126 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6128 cfg.href = this.href;
6131 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6134 // menu .. should add dropdown-menu class - so no need for carat..
6136 if (this.badge !== '') {
6138 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6143 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6147 href : this.href || "#",
6148 html: this.html || ''
6151 if (this.tagtype == 'a') {
6152 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6156 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6159 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6161 if(this.glyphicon) {
6162 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6167 cfg.cn[0].html += " <span class='caret'></span>";
6171 if (this.badge !== '') {
6173 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6181 onRender : function(ct, position)
6183 // Roo.log("Call onRender: " + this.xtype);
6184 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6188 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189 this.navLink = this.el.select('.nav-link',true).first();
6194 initEvents: function()
6196 if (typeof (this.menu) != 'undefined') {
6197 this.menu.parentType = this.xtype;
6198 this.menu.triggerEl = this.el;
6199 this.menu = this.addxtype(Roo.apply({}, this.menu));
6202 this.el.on('click', this.onClick, this);
6204 //if(this.tagtype == 'span'){
6205 // this.el.select('span',true).on('click', this.onClick, this);
6208 // at this point parent should be available..
6209 this.parent().register(this);
6212 onClick : function(e)
6214 if (e.getTarget('.dropdown-menu-item')) {
6215 // did you click on a menu itemm.... - then don't trigger onclick..
6220 this.preventDefault ||
6223 Roo.log("NavItem - prevent Default?");
6227 if (this.disabled) {
6231 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232 if (tg && tg.transition) {
6233 Roo.log("waiting for the transitionend");
6239 //Roo.log("fire event clicked");
6240 if(this.fireEvent('click', this, e) === false){
6244 if(this.tagtype == 'span'){
6248 //Roo.log(this.href);
6249 var ael = this.el.select('a',true).first();
6252 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255 return; // ignore... - it's a 'hash' to another page.
6257 Roo.log("NavItem - prevent Default?");
6259 this.scrollToElement(e);
6263 var p = this.parent();
6265 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266 if (typeof(p.setActiveItem) !== 'undefined') {
6267 p.setActiveItem(this);
6271 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273 // remove the collapsed menu expand...
6274 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6278 isActive: function () {
6281 setActive : function(state, fire, is_was_active)
6283 if (this.active && !state && this.navId) {
6284 this.was_active = true;
6285 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6287 nv.clearWasActive(this);
6291 this.active = state;
6294 this.el.removeClass('active');
6295 this.navLink ? this.navLink.removeClass('active') : false;
6296 } else if (!this.el.hasClass('active')) {
6298 this.el.addClass('active');
6299 if (Roo.bootstrap.version == 4 && this.navLink ) {
6300 this.navLink.addClass('active');
6305 this.fireEvent('changed', this, state);
6308 // show a panel if it's registered and related..
6310 if (!this.navId || !this.tabId || !state || is_was_active) {
6314 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6318 var pan = tg.getPanelByName(this.tabId);
6322 // if we can not flip to new panel - go back to old nav highlight..
6323 if (false == tg.showPanel(pan)) {
6324 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6326 var onav = nv.getWasActive();
6328 onav.setActive(true, false, true);
6337 // this should not be here...
6338 setDisabled : function(state)
6340 this.disabled = state;
6342 this.el.removeClass('disabled');
6343 } else if (!this.el.hasClass('disabled')) {
6344 this.el.addClass('disabled');
6350 * Fetch the element to display the tooltip on.
6351 * @return {Roo.Element} defaults to this.el
6353 tooltipEl : function()
6355 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6358 scrollToElement : function(e)
6360 var c = document.body;
6363 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6365 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366 c = document.documentElement;
6369 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6375 var o = target.calcOffsetsTo(c);
6382 this.fireEvent('scrollto', this, options, e);
6384 Roo.get(c).scrollTo('top', options.value, true);
6397 * <span> icon </span>
6398 * <span> text </span>
6399 * <span>badge </span>
6403 * @class Roo.bootstrap.NavSidebarItem
6404 * @extends Roo.bootstrap.NavItem
6405 * Bootstrap Navbar.NavSidebarItem class
6406 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407 * {Boolean} open is the menu open
6408 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410 * {String} buttonSize (sm|md|lg)the extra classes for the button
6411 * {Boolean} showArrow show arrow next to the text (default true)
6413 * Create a new Navbar Button
6414 * @param {Object} config The config object
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6422 * The raw click event for the entire grid.
6423 * @param {Roo.EventObject} e
6428 * Fires when the active item active state changes
6429 * @param {Roo.bootstrap.NavSidebarItem} this
6430 * @param {boolean} state the new state
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6440 badgeWeight : 'default',
6446 buttonWeight : 'default',
6452 getAutoCreate : function(){
6457 href : this.href || '#',
6463 if(this.buttonView){
6466 href : this.href || '#',
6467 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6480 cfg.cls += ' active';
6483 if (this.disabled) {
6484 cfg.cls += ' disabled';
6487 cfg.cls += ' open x-open';
6490 if (this.glyphicon || this.icon) {
6491 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6492 a.cn.push({ tag : 'i', cls : c }) ;
6495 if(!this.buttonView){
6498 html : this.html || ''
6505 if (this.badge !== '') {
6506 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6512 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6515 a.cls += ' dropdown-toggle treeview' ;
6521 initEvents : function()
6523 if (typeof (this.menu) != 'undefined') {
6524 this.menu.parentType = this.xtype;
6525 this.menu.triggerEl = this.el;
6526 this.menu = this.addxtype(Roo.apply({}, this.menu));
6529 this.el.on('click', this.onClick, this);
6531 if(this.badge !== ''){
6532 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6537 onClick : function(e)
6544 if(this.preventDefault){
6548 this.fireEvent('click', this, e);
6551 disable : function()
6553 this.setDisabled(true);
6558 this.setDisabled(false);
6561 setDisabled : function(state)
6563 if(this.disabled == state){
6567 this.disabled = state;
6570 this.el.addClass('disabled');
6574 this.el.removeClass('disabled');
6579 setActive : function(state)
6581 if(this.active == state){
6585 this.active = state;
6588 this.el.addClass('active');
6592 this.el.removeClass('active');
6597 isActive: function ()
6602 setBadge : function(str)
6608 this.badgeEl.dom.innerHTML = str;
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6627 * @class Roo.bootstrap.breadcrumb.Nav
6628 * @extends Roo.bootstrap.Component
6629 * Bootstrap Breadcrumb Nav Class
6631 * @children Roo.bootstrap.breadcrumb.Item
6634 * Create a new breadcrumb.Nav
6635 * @param {Object} config The config object
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6647 getAutoCreate : function()
6664 initEvents: function()
6666 this.olEl = this.el.select('ol',true).first();
6668 getChildContainer : function()
6684 * @class Roo.bootstrap.breadcrumb.Nav
6685 * @extends Roo.bootstrap.Component
6686 * Bootstrap Breadcrumb Nav Class
6688 * @children Roo.bootstrap.breadcrumb.Component
6689 * @cfg {String} html the content of the link.
6690 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691 * @cfg {Boolean} active is it active
6695 * Create a new breadcrumb.Nav
6696 * @param {Object} config The config object
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6705 * The img click event for the img.
6706 * @param {Roo.EventObject} e
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6718 getAutoCreate : function()
6723 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6725 if (this.href !== false) {
6732 cfg.html = this.html;
6738 initEvents: function()
6741 this.el.select('a', true).first().on('click',this.onClick, this)
6745 onClick : function(e)
6748 this.fireEvent('click',this, e);
6761 * @class Roo.bootstrap.Row
6762 * @extends Roo.bootstrap.Component
6763 * Bootstrap Row class (contains columns...)
6767 * @param {Object} config The config object
6770 Roo.bootstrap.Row = function(config){
6771 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6776 getAutoCreate : function(){
6795 * @class Roo.bootstrap.Pagination
6796 * @extends Roo.bootstrap.Component
6797 * Bootstrap Pagination class
6798 * @cfg {String} size xs | sm | md | lg
6799 * @cfg {Boolean} inverse false | true
6802 * Create a new Pagination
6803 * @param {Object} config The config object
6806 Roo.bootstrap.Pagination = function(config){
6807 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6816 getAutoCreate : function(){
6822 cfg.cls += ' inverse';
6828 cfg.cls += " " + this.cls;
6846 * @class Roo.bootstrap.PaginationItem
6847 * @extends Roo.bootstrap.Component
6848 * Bootstrap PaginationItem class
6849 * @cfg {String} html text
6850 * @cfg {String} href the link
6851 * @cfg {Boolean} preventDefault (true | false) default true
6852 * @cfg {Boolean} active (true | false) default false
6853 * @cfg {Boolean} disabled default false
6857 * Create a new PaginationItem
6858 * @param {Object} config The config object
6862 Roo.bootstrap.PaginationItem = function(config){
6863 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6868 * The raw click event for the entire grid.
6869 * @param {Roo.EventObject} e
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6879 preventDefault: true,
6884 getAutoCreate : function(){
6890 href : this.href ? this.href : '#',
6891 html : this.html ? this.html : ''
6901 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6905 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6911 initEvents: function() {
6913 this.el.on('click', this.onClick, this);
6916 onClick : function(e)
6918 Roo.log('PaginationItem on click ');
6919 if(this.preventDefault){
6927 this.fireEvent('click', this, e);
6943 * @class Roo.bootstrap.Slider
6944 * @extends Roo.bootstrap.Component
6945 * Bootstrap Slider class
6948 * Create a new Slider
6949 * @param {Object} config The config object
6952 Roo.bootstrap.Slider = function(config){
6953 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6958 getAutoCreate : function(){
6962 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6966 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6978 * Ext JS Library 1.1.1
6979 * Copyright(c) 2006-2007, Ext JS, LLC.
6981 * Originally Released Under LGPL - original licence link has changed is not relivant.
6984 * <script type="text/javascript">
6989 * @class Roo.grid.ColumnModel
6990 * @extends Roo.util.Observable
6991 * This is the default implementation of a ColumnModel used by the Grid. It defines
6992 * the columns in the grid.
6995 var colModel = new Roo.grid.ColumnModel([
6996 {header: "Ticker", width: 60, sortable: true, locked: true},
6997 {header: "Company Name", width: 150, sortable: true},
6998 {header: "Market Cap.", width: 100, sortable: true},
6999 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000 {header: "Employees", width: 100, sortable: true, resizable: false}
7005 * The config options listed for this class are options which may appear in each
7006 * individual column definition.
7007 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7009 * @param {Object} config An Array of column config objects. See this class's
7010 * config objects for details.
7012 Roo.grid.ColumnModel = function(config){
7014 * The config passed into the constructor
7016 this.config = config;
7019 // if no id, create one
7020 // if the column does not have a dataIndex mapping,
7021 // map it to the order it is in the config
7022 for(var i = 0, len = config.length; i < len; i++){
7024 if(typeof c.dataIndex == "undefined"){
7027 if(typeof c.renderer == "string"){
7028 c.renderer = Roo.util.Format[c.renderer];
7030 if(typeof c.id == "undefined"){
7033 if(c.editor && c.editor.xtype){
7034 c.editor = Roo.factory(c.editor, Roo.grid);
7036 if(c.editor && c.editor.isFormField){
7037 c.editor = new Roo.grid.GridEditor(c.editor);
7039 this.lookup[c.id] = c;
7043 * The width of columns which have no width specified (defaults to 100)
7046 this.defaultWidth = 100;
7049 * Default sortable of columns which have no sortable specified (defaults to false)
7052 this.defaultSortable = false;
7056 * @event widthchange
7057 * Fires when the width of a column changes.
7058 * @param {ColumnModel} this
7059 * @param {Number} columnIndex The column index
7060 * @param {Number} newWidth The new width
7062 "widthchange": true,
7064 * @event headerchange
7065 * Fires when the text of a header changes.
7066 * @param {ColumnModel} this
7067 * @param {Number} columnIndex The column index
7068 * @param {Number} newText The new header text
7070 "headerchange": true,
7072 * @event hiddenchange
7073 * Fires when a column is hidden or "unhidden".
7074 * @param {ColumnModel} this
7075 * @param {Number} columnIndex The column index
7076 * @param {Boolean} hidden true if hidden, false otherwise
7078 "hiddenchange": true,
7080 * @event columnmoved
7081 * Fires when a column is moved.
7082 * @param {ColumnModel} this
7083 * @param {Number} oldIndex
7084 * @param {Number} newIndex
7086 "columnmoved" : true,
7088 * @event columlockchange
7089 * Fires when a column's locked state is changed
7090 * @param {ColumnModel} this
7091 * @param {Number} colIndex
7092 * @param {Boolean} locked true if locked
7094 "columnlockchange" : true
7096 Roo.grid.ColumnModel.superclass.constructor.call(this);
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7100 * @cfg {String} header The header text to display in the Grid view.
7103 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105 * specified, the column's index is used as an index into the Record's data Array.
7108 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7112 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113 * Defaults to the value of the {@link #defaultSortable} property.
7114 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7117 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7120 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7123 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7126 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7129 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7135 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7138 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7141 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7144 * @cfg {String} cursor (Optional)
7147 * @cfg {String} tooltip (Optional)
7150 * @cfg {Number} xs (Optional)
7153 * @cfg {Number} sm (Optional)
7156 * @cfg {Number} md (Optional)
7159 * @cfg {Number} lg (Optional)
7162 * Returns the id of the column at the specified index.
7163 * @param {Number} index The column index
7164 * @return {String} the id
7166 getColumnId : function(index){
7167 return this.config[index].id;
7171 * Returns the column for a specified id.
7172 * @param {String} id The column id
7173 * @return {Object} the column
7175 getColumnById : function(id){
7176 return this.lookup[id];
7181 * Returns the column for a specified dataIndex.
7182 * @param {String} dataIndex The column dataIndex
7183 * @return {Object|Boolean} the column or false if not found
7185 getColumnByDataIndex: function(dataIndex){
7186 var index = this.findColumnIndex(dataIndex);
7187 return index > -1 ? this.config[index] : false;
7191 * Returns the index for a specified column id.
7192 * @param {String} id The column id
7193 * @return {Number} the index, or -1 if not found
7195 getIndexById : function(id){
7196 for(var i = 0, len = this.config.length; i < len; i++){
7197 if(this.config[i].id == id){
7205 * Returns the index for a specified column dataIndex.
7206 * @param {String} dataIndex The column dataIndex
7207 * @return {Number} the index, or -1 if not found
7210 findColumnIndex : function(dataIndex){
7211 for(var i = 0, len = this.config.length; i < len; i++){
7212 if(this.config[i].dataIndex == dataIndex){
7220 moveColumn : function(oldIndex, newIndex){
7221 var c = this.config[oldIndex];
7222 this.config.splice(oldIndex, 1);
7223 this.config.splice(newIndex, 0, c);
7224 this.dataMap = null;
7225 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7228 isLocked : function(colIndex){
7229 return this.config[colIndex].locked === true;
7232 setLocked : function(colIndex, value, suppressEvent){
7233 if(this.isLocked(colIndex) == value){
7236 this.config[colIndex].locked = value;
7238 this.fireEvent("columnlockchange", this, colIndex, value);
7242 getTotalLockedWidth : function(){
7244 for(var i = 0; i < this.config.length; i++){
7245 if(this.isLocked(i) && !this.isHidden(i)){
7246 this.totalWidth += this.getColumnWidth(i);
7252 getLockedCount : function(){
7253 for(var i = 0, len = this.config.length; i < len; i++){
7254 if(!this.isLocked(i)){
7259 return this.config.length;
7263 * Returns the number of columns.
7266 getColumnCount : function(visibleOnly){
7267 if(visibleOnly === true){
7269 for(var i = 0, len = this.config.length; i < len; i++){
7270 if(!this.isHidden(i)){
7276 return this.config.length;
7280 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281 * @param {Function} fn
7282 * @param {Object} scope (optional)
7283 * @return {Array} result
7285 getColumnsBy : function(fn, scope){
7287 for(var i = 0, len = this.config.length; i < len; i++){
7288 var c = this.config[i];
7289 if(fn.call(scope||this, c, i) === true){
7297 * Returns true if the specified column is sortable.
7298 * @param {Number} col The column index
7301 isSortable : function(col){
7302 if(typeof this.config[col].sortable == "undefined"){
7303 return this.defaultSortable;
7305 return this.config[col].sortable;
7309 * Returns the rendering (formatting) function defined for the column.
7310 * @param {Number} col The column index.
7311 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7313 getRenderer : function(col){
7314 if(!this.config[col].renderer){
7315 return Roo.grid.ColumnModel.defaultRenderer;
7317 return this.config[col].renderer;
7321 * Sets the rendering (formatting) function for a column.
7322 * @param {Number} col The column index
7323 * @param {Function} fn The function to use to process the cell's raw data
7324 * to return HTML markup for the grid view. The render function is called with
7325 * the following parameters:<ul>
7326 * <li>Data value.</li>
7327 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328 * <li>css A CSS style string to apply to the table cell.</li>
7329 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331 * <li>Row index</li>
7332 * <li>Column index</li>
7333 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7335 setRenderer : function(col, fn){
7336 this.config[col].renderer = fn;
7340 * Returns the width for the specified column.
7341 * @param {Number} col The column index
7344 getColumnWidth : function(col){
7345 return this.config[col].width * 1 || this.defaultWidth;
7349 * Sets the width for a column.
7350 * @param {Number} col The column index
7351 * @param {Number} width The new width
7353 setColumnWidth : function(col, width, suppressEvent){
7354 this.config[col].width = width;
7355 this.totalWidth = null;
7357 this.fireEvent("widthchange", this, col, width);
7362 * Returns the total width of all columns.
7363 * @param {Boolean} includeHidden True to include hidden column widths
7366 getTotalWidth : function(includeHidden){
7367 if(!this.totalWidth){
7368 this.totalWidth = 0;
7369 for(var i = 0, len = this.config.length; i < len; i++){
7370 if(includeHidden || !this.isHidden(i)){
7371 this.totalWidth += this.getColumnWidth(i);
7375 return this.totalWidth;
7379 * Returns the header for the specified column.
7380 * @param {Number} col The column index
7383 getColumnHeader : function(col){
7384 return this.config[col].header;
7388 * Sets the header for a column.
7389 * @param {Number} col The column index
7390 * @param {String} header The new header
7392 setColumnHeader : function(col, header){
7393 this.config[col].header = header;
7394 this.fireEvent("headerchange", this, col, header);
7398 * Returns the tooltip for the specified column.
7399 * @param {Number} col The column index
7402 getColumnTooltip : function(col){
7403 return this.config[col].tooltip;
7406 * Sets the tooltip for a column.
7407 * @param {Number} col The column index
7408 * @param {String} tooltip The new tooltip
7410 setColumnTooltip : function(col, tooltip){
7411 this.config[col].tooltip = tooltip;
7415 * Returns the dataIndex for the specified column.
7416 * @param {Number} col The column index
7419 getDataIndex : function(col){
7420 return this.config[col].dataIndex;
7424 * Sets the dataIndex for a column.
7425 * @param {Number} col The column index
7426 * @param {Number} dataIndex The new dataIndex
7428 setDataIndex : function(col, dataIndex){
7429 this.config[col].dataIndex = dataIndex;
7435 * Returns true if the cell is editable.
7436 * @param {Number} colIndex The column index
7437 * @param {Number} rowIndex The row index - this is nto actually used..?
7440 isCellEditable : function(colIndex, rowIndex){
7441 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7445 * Returns the editor defined for the cell/column.
7446 * return false or null to disable editing.
7447 * @param {Number} colIndex The column index
7448 * @param {Number} rowIndex The row index
7451 getCellEditor : function(colIndex, rowIndex){
7452 return this.config[colIndex].editor;
7456 * Sets if a column is editable.
7457 * @param {Number} col The column index
7458 * @param {Boolean} editable True if the column is editable
7460 setEditable : function(col, editable){
7461 this.config[col].editable = editable;
7466 * Returns true if the column is hidden.
7467 * @param {Number} colIndex The column index
7470 isHidden : function(colIndex){
7471 return this.config[colIndex].hidden;
7476 * Returns true if the column width cannot be changed
7478 isFixed : function(colIndex){
7479 return this.config[colIndex].fixed;
7483 * Returns true if the column can be resized
7486 isResizable : function(colIndex){
7487 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7490 * Sets if a column is hidden.
7491 * @param {Number} colIndex The column index
7492 * @param {Boolean} hidden True if the column is hidden
7494 setHidden : function(colIndex, hidden){
7495 this.config[colIndex].hidden = hidden;
7496 this.totalWidth = null;
7497 this.fireEvent("hiddenchange", this, colIndex, hidden);
7501 * Sets the editor for a column.
7502 * @param {Number} col The column index
7503 * @param {Object} editor The editor object
7505 setEditor : function(col, editor){
7506 this.config[col].editor = editor;
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7512 if(typeof value == "object") {
7515 if(typeof value == "string" && value.length < 1){
7519 return String.format("{0}", value);
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7526 * Ext JS Library 1.1.1
7527 * Copyright(c) 2006-2007, Ext JS, LLC.
7529 * Originally Released Under LGPL - original licence link has changed is not relivant.
7532 * <script type="text/javascript">
7536 * @class Roo.LoadMask
7537 * A simple utility class for generically masking elements while loading data. If the element being masked has
7538 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7540 * element's UpdateManager load indicator and will be destroyed after the initial load.
7542 * Create a new LoadMask
7543 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544 * @param {Object} config The config object
7546 Roo.LoadMask = function(el, config){
7547 this.el = Roo.get(el);
7548 Roo.apply(this, config);
7550 this.store.on('beforeload', this.onBeforeLoad, this);
7551 this.store.on('load', this.onLoad, this);
7552 this.store.on('loadexception', this.onLoadException, this);
7553 this.removeMask = false;
7555 var um = this.el.getUpdateManager();
7556 um.showLoadIndicator = false; // disable the default indicator
7557 um.on('beforeupdate', this.onBeforeLoad, this);
7558 um.on('update', this.onLoad, this);
7559 um.on('failure', this.onLoad, this);
7560 this.removeMask = true;
7564 Roo.LoadMask.prototype = {
7566 * @cfg {Boolean} removeMask
7567 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7572 * The text to display in a centered loading message box (defaults to 'Loading...')
7576 * @cfg {String} msgCls
7577 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7579 msgCls : 'x-mask-loading',
7582 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7588 * Disables the mask to prevent it from being displayed
7590 disable : function(){
7591 this.disabled = true;
7595 * Enables the mask so that it can be displayed
7597 enable : function(){
7598 this.disabled = false;
7601 onLoadException : function()
7605 if (typeof(arguments[3]) != 'undefined') {
7606 Roo.MessageBox.alert("Error loading",arguments[3]);
7610 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7618 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7623 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7627 onBeforeLoad : function(){
7629 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7634 destroy : function(){
7636 this.store.un('beforeload', this.onBeforeLoad, this);
7637 this.store.un('load', this.onLoad, this);
7638 this.store.un('loadexception', this.onLoadException, this);
7640 var um = this.el.getUpdateManager();
7641 um.un('beforeupdate', this.onBeforeLoad, this);
7642 um.un('update', this.onLoad, this);
7643 um.un('failure', this.onLoad, this);
7654 * @class Roo.bootstrap.Table
7655 * @extends Roo.bootstrap.Component
7656 * Bootstrap Table class
7657 * @cfg {String} cls table class
7658 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659 * @cfg {String} bgcolor Specifies the background color for a table
7660 * @cfg {Number} border Specifies whether the table cells should have borders or not
7661 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662 * @cfg {Number} cellspacing Specifies the space between cells
7663 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665 * @cfg {String} sortable Specifies that the table should be sortable
7666 * @cfg {String} summary Specifies a summary of the content of a table
7667 * @cfg {Number} width Specifies the width of a table
7668 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7670 * @cfg {boolean} striped Should the rows be alternative striped
7671 * @cfg {boolean} bordered Add borders to the table
7672 * @cfg {boolean} hover Add hover highlighting
7673 * @cfg {boolean} condensed Format condensed
7674 * @cfg {boolean} responsive Format condensed
7675 * @cfg {Boolean} loadMask (true|false) default false
7676 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678 * @cfg {Boolean} rowSelection (true|false) default false
7679 * @cfg {Boolean} cellSelection (true|false) default false
7680 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7682 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7683 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7687 * Create a new Table
7688 * @param {Object} config The config object
7691 Roo.bootstrap.Table = function(config){
7692 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7697 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7702 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7704 this.sm.grid = this;
7705 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706 this.sm = this.selModel;
7707 this.sm.xmodule = this.xmodule || false;
7710 if (this.cm && typeof(this.cm.config) == 'undefined') {
7711 this.colModel = new Roo.grid.ColumnModel(this.cm);
7712 this.cm = this.colModel;
7713 this.cm.xmodule = this.xmodule || false;
7716 this.store= Roo.factory(this.store, Roo.data);
7717 this.ds = this.store;
7718 this.ds.xmodule = this.xmodule || false;
7721 if (this.footer && this.store) {
7722 this.footer.dataSource = this.ds;
7723 this.footer = Roo.factory(this.footer);
7730 * Fires when a cell is clicked
7731 * @param {Roo.bootstrap.Table} this
7732 * @param {Roo.Element} el
7733 * @param {Number} rowIndex
7734 * @param {Number} columnIndex
7735 * @param {Roo.EventObject} e
7739 * @event celldblclick
7740 * Fires when a cell is double clicked
7741 * @param {Roo.bootstrap.Table} this
7742 * @param {Roo.Element} el
7743 * @param {Number} rowIndex
7744 * @param {Number} columnIndex
7745 * @param {Roo.EventObject} e
7747 "celldblclick" : true,
7750 * Fires when a row is clicked
7751 * @param {Roo.bootstrap.Table} this
7752 * @param {Roo.Element} el
7753 * @param {Number} rowIndex
7754 * @param {Roo.EventObject} e
7758 * @event rowdblclick
7759 * Fires when a row is double clicked
7760 * @param {Roo.bootstrap.Table} this
7761 * @param {Roo.Element} el
7762 * @param {Number} rowIndex
7763 * @param {Roo.EventObject} e
7765 "rowdblclick" : true,
7768 * Fires when a mouseover occur
7769 * @param {Roo.bootstrap.Table} this
7770 * @param {Roo.Element} el
7771 * @param {Number} rowIndex
7772 * @param {Number} columnIndex
7773 * @param {Roo.EventObject} e
7778 * Fires when a mouseout occur
7779 * @param {Roo.bootstrap.Table} this
7780 * @param {Roo.Element} el
7781 * @param {Number} rowIndex
7782 * @param {Number} columnIndex
7783 * @param {Roo.EventObject} e
7788 * Fires when a row is rendered, so you can change add a style to it.
7789 * @param {Roo.bootstrap.Table} this
7790 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7794 * @event rowsrendered
7795 * Fires when all the rows have been rendered
7796 * @param {Roo.bootstrap.Table} this
7798 'rowsrendered' : true,
7800 * @event contextmenu
7801 * The raw contextmenu event for the entire grid.
7802 * @param {Roo.EventObject} e
7804 "contextmenu" : true,
7806 * @event rowcontextmenu
7807 * Fires when a row is right clicked
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Number} rowIndex
7810 * @param {Roo.EventObject} e
7812 "rowcontextmenu" : true,
7814 * @event cellcontextmenu
7815 * Fires when a cell is right clicked
7816 * @param {Roo.bootstrap.Table} this
7817 * @param {Number} rowIndex
7818 * @param {Number} cellIndex
7819 * @param {Roo.EventObject} e
7821 "cellcontextmenu" : true,
7823 * @event headercontextmenu
7824 * Fires when a header is right clicked
7825 * @param {Roo.bootstrap.Table} this
7826 * @param {Number} columnIndex
7827 * @param {Roo.EventObject} e
7829 "headercontextmenu" : true
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7859 rowSelection : false,
7860 cellSelection : false,
7863 // Roo.Element - the tbody
7865 // Roo.Element - thead element
7868 container: false, // used by gridpanel...
7874 auto_hide_footer : false,
7876 getAutoCreate : function()
7878 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7885 if (this.scrollBody) {
7886 cfg.cls += ' table-body-fixed';
7889 cfg.cls += ' table-striped';
7893 cfg.cls += ' table-hover';
7895 if (this.bordered) {
7896 cfg.cls += ' table-bordered';
7898 if (this.condensed) {
7899 cfg.cls += ' table-condensed';
7901 if (this.responsive) {
7902 cfg.cls += ' table-responsive';
7906 cfg.cls+= ' ' +this.cls;
7909 // this lot should be simplifed...
7922 ].forEach(function(k) {
7930 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7933 if(this.store || this.cm){
7934 if(this.headerShow){
7935 cfg.cn.push(this.renderHeader());
7938 cfg.cn.push(this.renderBody());
7940 if(this.footerShow){
7941 cfg.cn.push(this.renderFooter());
7943 // where does this come from?
7944 //cfg.cls+= ' TableGrid';
7947 return { cn : [ cfg ] };
7950 initEvents : function()
7952 if(!this.store || !this.cm){
7955 if (this.selModel) {
7956 this.selModel.initEvents();
7960 //Roo.log('initEvents with ds!!!!');
7962 this.mainBody = this.el.select('tbody', true).first();
7963 this.mainHead = this.el.select('thead', true).first();
7964 this.mainFoot = this.el.select('tfoot', true).first();
7970 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971 e.on('click', _this.sort, _this);
7974 this.mainBody.on("click", this.onClick, this);
7975 this.mainBody.on("dblclick", this.onDblClick, this);
7977 // why is this done????? = it breaks dialogs??
7978 //this.parent().el.setStyle('position', 'relative');
7982 this.footer.parentId = this.id;
7983 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7986 this.el.select('tfoot tr td').first().addClass('hide');
7991 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7994 this.store.on('load', this.onLoad, this);
7995 this.store.on('beforeload', this.onBeforeLoad, this);
7996 this.store.on('update', this.onUpdate, this);
7997 this.store.on('add', this.onAdd, this);
7998 this.store.on("clear", this.clear, this);
8000 this.el.on("contextmenu", this.onContextMenu, this);
8002 this.mainBody.on('scroll', this.onBodyScroll, this);
8004 this.cm.on("headerchange", this.onHeaderChange, this);
8006 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8010 onContextMenu : function(e, t)
8012 this.processEvent("contextmenu", e);
8015 processEvent : function(name, e)
8017 if (name != 'touchstart' ) {
8018 this.fireEvent(name, e);
8021 var t = e.getTarget();
8023 var cell = Roo.get(t);
8029 if(cell.findParent('tfoot', false, true)){
8033 if(cell.findParent('thead', false, true)){
8035 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036 cell = Roo.get(t).findParent('th', false, true);
8038 Roo.log("failed to find th in thead?");
8039 Roo.log(e.getTarget());
8044 var cellIndex = cell.dom.cellIndex;
8046 var ename = name == 'touchstart' ? 'click' : name;
8047 this.fireEvent("header" + ename, this, cellIndex, e);
8052 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053 cell = Roo.get(t).findParent('td', false, true);
8055 Roo.log("failed to find th in tbody?");
8056 Roo.log(e.getTarget());
8061 var row = cell.findParent('tr', false, true);
8062 var cellIndex = cell.dom.cellIndex;
8063 var rowIndex = row.dom.rowIndex - 1;
8067 this.fireEvent("row" + name, this, rowIndex, e);
8071 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8077 onMouseover : function(e, el)
8079 var cell = Roo.get(el);
8085 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086 cell = cell.findParent('td', false, true);
8089 var row = cell.findParent('tr', false, true);
8090 var cellIndex = cell.dom.cellIndex;
8091 var rowIndex = row.dom.rowIndex - 1; // start from 0
8093 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8097 onMouseout : function(e, el)
8099 var cell = Roo.get(el);
8105 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106 cell = cell.findParent('td', false, true);
8109 var row = cell.findParent('tr', false, true);
8110 var cellIndex = cell.dom.cellIndex;
8111 var rowIndex = row.dom.rowIndex - 1; // start from 0
8113 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8117 onClick : function(e, el)
8119 var cell = Roo.get(el);
8121 if(!cell || (!this.cellSelection && !this.rowSelection)){
8125 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126 cell = cell.findParent('td', false, true);
8129 if(!cell || typeof(cell) == 'undefined'){
8133 var row = cell.findParent('tr', false, true);
8135 if(!row || typeof(row) == 'undefined'){
8139 var cellIndex = cell.dom.cellIndex;
8140 var rowIndex = this.getRowIndex(row);
8142 // why??? - should these not be based on SelectionModel?
8143 if(this.cellSelection){
8144 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8147 if(this.rowSelection){
8148 this.fireEvent('rowclick', this, row, rowIndex, e);
8154 onDblClick : function(e,el)
8156 var cell = Roo.get(el);
8158 if(!cell || (!this.cellSelection && !this.rowSelection)){
8162 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163 cell = cell.findParent('td', false, true);
8166 if(!cell || typeof(cell) == 'undefined'){
8170 var row = cell.findParent('tr', false, true);
8172 if(!row || typeof(row) == 'undefined'){
8176 var cellIndex = cell.dom.cellIndex;
8177 var rowIndex = this.getRowIndex(row);
8179 if(this.cellSelection){
8180 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8183 if(this.rowSelection){
8184 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8188 sort : function(e,el)
8190 var col = Roo.get(el);
8192 if(!col.hasClass('sortable')){
8196 var sort = col.attr('sort');
8199 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8203 this.store.sortInfo = {field : sort, direction : dir};
8206 Roo.log("calling footer first");
8207 this.footer.onClick('first');
8210 this.store.load({ params : { start : 0 } });
8214 renderHeader : function()
8222 this.totalWidth = 0;
8224 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8226 var config = cm.config[i];
8230 cls : 'x-hcol-' + i,
8232 html: cm.getColumnHeader(i)
8237 if(typeof(config.sortable) != 'undefined' && config.sortable){
8239 c.html = '<i class="glyphicon"></i>' + c.html;
8242 // could use BS4 hidden-..-down
8244 if(typeof(config.lgHeader) != 'undefined'){
8245 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8248 if(typeof(config.mdHeader) != 'undefined'){
8249 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8252 if(typeof(config.smHeader) != 'undefined'){
8253 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8256 if(typeof(config.xsHeader) != 'undefined'){
8257 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8264 if(typeof(config.tooltip) != 'undefined'){
8265 c.tooltip = config.tooltip;
8268 if(typeof(config.colspan) != 'undefined'){
8269 c.colspan = config.colspan;
8272 if(typeof(config.hidden) != 'undefined' && config.hidden){
8273 c.style += ' display:none;';
8276 if(typeof(config.dataIndex) != 'undefined'){
8277 c.sort = config.dataIndex;
8282 if(typeof(config.align) != 'undefined' && config.align.length){
8283 c.style += ' text-align:' + config.align + ';';
8286 if(typeof(config.width) != 'undefined'){
8287 c.style += ' width:' + config.width + 'px;';
8288 this.totalWidth += config.width;
8290 this.totalWidth += 100; // assume minimum of 100 per column?
8293 if(typeof(config.cls) != 'undefined'){
8294 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8297 ['xs','sm','md','lg'].map(function(size){
8299 if(typeof(config[size]) == 'undefined'){
8303 if (!config[size]) { // 0 = hidden
8304 // BS 4 '0' is treated as hide that column and below.
8305 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8309 c.cls += ' col-' + size + '-' + config[size] + (
8310 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8322 renderBody : function()
8332 colspan : this.cm.getColumnCount()
8342 renderFooter : function()
8352 colspan : this.cm.getColumnCount()
8366 // Roo.log('ds onload');
8371 var ds = this.store;
8373 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375 if (_this.store.sortInfo) {
8377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378 e.select('i', true).addClass(['glyphicon-arrow-up']);
8381 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382 e.select('i', true).addClass(['glyphicon-arrow-down']);
8387 var tbody = this.mainBody;
8389 if(ds.getCount() > 0){
8390 ds.data.each(function(d,rowIndex){
8391 var row = this.renderRow(cm, ds, rowIndex);
8393 tbody.createChild(row);
8397 if(row.cellObjects.length){
8398 Roo.each(row.cellObjects, function(r){
8399 _this.renderCellObject(r);
8406 var tfoot = this.el.select('tfoot', true).first();
8408 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8410 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8412 var total = this.ds.getTotalCount();
8414 if(this.footer.pageSize < total){
8415 this.mainFoot.show();
8419 Roo.each(this.el.select('tbody td', true).elements, function(e){
8420 e.on('mouseover', _this.onMouseover, _this);
8423 Roo.each(this.el.select('tbody td', true).elements, function(e){
8424 e.on('mouseout', _this.onMouseout, _this);
8426 this.fireEvent('rowsrendered', this);
8432 onUpdate : function(ds,record)
8434 this.refreshRow(record);
8438 onRemove : function(ds, record, index, isUpdate){
8439 if(isUpdate !== true){
8440 this.fireEvent("beforerowremoved", this, index, record);
8442 var bt = this.mainBody.dom;
8444 var rows = this.el.select('tbody > tr', true).elements;
8446 if(typeof(rows[index]) != 'undefined'){
8447 bt.removeChild(rows[index].dom);
8450 // if(bt.rows[index]){
8451 // bt.removeChild(bt.rows[index]);
8454 if(isUpdate !== true){
8455 //this.stripeRows(index);
8456 //this.syncRowHeights(index, index);
8458 this.fireEvent("rowremoved", this, index, record);
8462 onAdd : function(ds, records, rowIndex)
8464 //Roo.log('on Add called');
8465 // - note this does not handle multiple adding very well..
8466 var bt = this.mainBody.dom;
8467 for (var i =0 ; i < records.length;i++) {
8468 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469 //Roo.log(records[i]);
8470 //Roo.log(this.store.getAt(rowIndex+i));
8471 this.insertRow(this.store, rowIndex + i, false);
8478 refreshRow : function(record){
8479 var ds = this.store, index;
8480 if(typeof record == 'number'){
8482 record = ds.getAt(index);
8484 index = ds.indexOf(record);
8486 return; // should not happen - but seems to
8489 this.insertRow(ds, index, true);
8491 this.onRemove(ds, record, index+1, true);
8493 //this.syncRowHeights(index, index);
8495 this.fireEvent("rowupdated", this, index, record);
8498 insertRow : function(dm, rowIndex, isUpdate){
8501 this.fireEvent("beforerowsinserted", this, rowIndex);
8503 //var s = this.getScrollState();
8504 var row = this.renderRow(this.cm, this.store, rowIndex);
8505 // insert before rowIndex..
8506 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8510 if(row.cellObjects.length){
8511 Roo.each(row.cellObjects, function(r){
8512 _this.renderCellObject(r);
8517 this.fireEvent("rowsinserted", this, rowIndex);
8518 //this.syncRowHeights(firstRow, lastRow);
8519 //this.stripeRows(firstRow);
8526 getRowDom : function(rowIndex)
8528 var rows = this.el.select('tbody > tr', true).elements;
8530 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8533 // returns the object tree for a tr..
8536 renderRow : function(cm, ds, rowIndex)
8538 var d = ds.getAt(rowIndex);
8542 cls : 'x-row-' + rowIndex,
8546 var cellObjects = [];
8548 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549 var config = cm.config[i];
8551 var renderer = cm.getRenderer(i);
8555 if(typeof(renderer) !== 'undefined'){
8556 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8558 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559 // and are rendered into the cells after the row is rendered - using the id for the element.
8561 if(typeof(value) === 'object'){
8571 rowIndex : rowIndex,
8576 this.fireEvent('rowclass', this, rowcfg);
8580 cls : rowcfg.rowClass + ' x-col-' + i,
8582 html: (typeof(value) === 'object') ? '' : value
8589 if(typeof(config.colspan) != 'undefined'){
8590 td.colspan = config.colspan;
8593 if(typeof(config.hidden) != 'undefined' && config.hidden){
8594 td.style += ' display:none;';
8597 if(typeof(config.align) != 'undefined' && config.align.length){
8598 td.style += ' text-align:' + config.align + ';';
8600 if(typeof(config.valign) != 'undefined' && config.valign.length){
8601 td.style += ' vertical-align:' + config.valign + ';';
8604 if(typeof(config.width) != 'undefined'){
8605 td.style += ' width:' + config.width + 'px;';
8608 if(typeof(config.cursor) != 'undefined'){
8609 td.style += ' cursor:' + config.cursor + ';';
8612 if(typeof(config.cls) != 'undefined'){
8613 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8616 ['xs','sm','md','lg'].map(function(size){
8618 if(typeof(config[size]) == 'undefined'){
8624 if (!config[size]) { // 0 = hidden
8625 // BS 4 '0' is treated as hide that column and below.
8626 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8630 td.cls += ' col-' + size + '-' + config[size] + (
8631 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8641 row.cellObjects = cellObjects;
8649 onBeforeLoad : function()
8658 this.el.select('tbody', true).first().dom.innerHTML = '';
8661 * Show or hide a row.
8662 * @param {Number} rowIndex to show or hide
8663 * @param {Boolean} state hide
8665 setRowVisibility : function(rowIndex, state)
8667 var bt = this.mainBody.dom;
8669 var rows = this.el.select('tbody > tr', true).elements;
8671 if(typeof(rows[rowIndex]) == 'undefined'){
8674 rows[rowIndex].dom.style.display = state ? '' : 'none';
8678 getSelectionModel : function(){
8680 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8682 return this.selModel;
8685 * Render the Roo.bootstrap object from renderder
8687 renderCellObject : function(r)
8691 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8693 var t = r.cfg.render(r.container);
8696 Roo.each(r.cfg.cn, function(c){
8698 container: t.getChildContainer(),
8701 _this.renderCellObject(child);
8706 getRowIndex : function(row)
8710 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8721 * Returns the grid's underlying element = used by panel.Grid
8722 * @return {Element} The element
8724 getGridEl : function(){
8728 * Forces a resize - used by panel.Grid
8729 * @return {Element} The element
8731 autoSize : function()
8733 //var ctr = Roo.get(this.container.dom.parentElement);
8734 var ctr = Roo.get(this.el.dom);
8736 var thd = this.getGridEl().select('thead',true).first();
8737 var tbd = this.getGridEl().select('tbody', true).first();
8738 var tfd = this.getGridEl().select('tfoot', true).first();
8740 var cw = ctr.getWidth();
8741 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8745 tbd.setWidth(ctr.getWidth());
8746 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747 // this needs fixing for various usage - currently only hydra job advers I think..
8749 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8751 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8754 cw = Math.max(cw, this.totalWidth);
8755 this.getGridEl().select('tbody tr',true).setWidth(cw);
8757 // resize 'expandable coloumn?
8759 return; // we doe not have a view in this design..
8762 onBodyScroll: function()
8764 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8766 this.mainHead.setStyle({
8767 'position' : 'relative',
8768 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8774 var scrollHeight = this.mainBody.dom.scrollHeight;
8776 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8778 var height = this.mainBody.getHeight();
8780 if(scrollHeight - height == scrollTop) {
8782 var total = this.ds.getTotalCount();
8784 if(this.footer.cursor + this.footer.pageSize < total){
8786 this.footer.ds.load({
8788 start : this.footer.cursor + this.footer.pageSize,
8789 limit : this.footer.pageSize
8799 onHeaderChange : function()
8801 var header = this.renderHeader();
8802 var table = this.el.select('table', true).first();
8804 this.mainHead.remove();
8805 this.mainHead = table.createChild(header, this.mainBody, false);
8808 onHiddenChange : function(colModel, colIndex, hidden)
8810 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8813 this.CSS.updateRule(thSelector, "display", "");
8814 this.CSS.updateRule(tdSelector, "display", "");
8817 this.CSS.updateRule(thSelector, "display", "none");
8818 this.CSS.updateRule(tdSelector, "display", "none");
8821 this.onHeaderChange();
8825 setColumnWidth: function(col_index, width)
8827 // width = "md-2 xs-2..."
8828 if(!this.colModel.config[col_index]) {
8832 var w = width.split(" ");
8834 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8836 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8839 for(var j = 0; j < w.length; j++) {
8845 var size_cls = w[j].split("-");
8847 if(!Number.isInteger(size_cls[1] * 1)) {
8851 if(!this.colModel.config[col_index][size_cls[0]]) {
8855 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8859 h_row[0].classList.replace(
8860 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861 "col-"+size_cls[0]+"-"+size_cls[1]
8864 for(var i = 0; i < rows.length; i++) {
8866 var size_cls = w[j].split("-");
8868 if(!Number.isInteger(size_cls[1] * 1)) {
8872 if(!this.colModel.config[col_index][size_cls[0]]) {
8876 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8880 rows[i].classList.replace(
8881 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882 "col-"+size_cls[0]+"-"+size_cls[1]
8886 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8901 * @class Roo.bootstrap.TableCell
8902 * @extends Roo.bootstrap.Component
8903 * Bootstrap TableCell class
8904 * @cfg {String} html cell contain text
8905 * @cfg {String} cls cell class
8906 * @cfg {String} tag cell tag (td|th) default td
8907 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908 * @cfg {String} align Aligns the content in a cell
8909 * @cfg {String} axis Categorizes cells
8910 * @cfg {String} bgcolor Specifies the background color of a cell
8911 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912 * @cfg {Number} colspan Specifies the number of columns a cell should span
8913 * @cfg {String} headers Specifies one or more header cells a cell is related to
8914 * @cfg {Number} height Sets the height of a cell
8915 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916 * @cfg {Number} rowspan Sets the number of rows a cell should span
8917 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918 * @cfg {String} valign Vertical aligns the content in a cell
8919 * @cfg {Number} width Specifies the width of a cell
8922 * Create a new TableCell
8923 * @param {Object} config The config object
8926 Roo.bootstrap.TableCell = function(config){
8927 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8950 getAutoCreate : function(){
8951 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8971 cfg.align=this.align
8977 cfg.bgcolor=this.bgcolor
8980 cfg.charoff=this.charoff
8983 cfg.colspan=this.colspan
8986 cfg.headers=this.headers
8989 cfg.height=this.height
8992 cfg.nowrap=this.nowrap
8995 cfg.rowspan=this.rowspan
8998 cfg.scope=this.scope
9001 cfg.valign=this.valign
9004 cfg.width=this.width
9023 * @class Roo.bootstrap.TableRow
9024 * @extends Roo.bootstrap.Component
9025 * Bootstrap TableRow class
9026 * @cfg {String} cls row class
9027 * @cfg {String} align Aligns the content in a table row
9028 * @cfg {String} bgcolor Specifies a background color for a table row
9029 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030 * @cfg {String} valign Vertical aligns the content in a table row
9033 * Create a new TableRow
9034 * @param {Object} config The config object
9037 Roo.bootstrap.TableRow = function(config){
9038 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9049 getAutoCreate : function(){
9050 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9060 cfg.align = this.align;
9063 cfg.bgcolor = this.bgcolor;
9066 cfg.charoff = this.charoff;
9069 cfg.valign = this.valign;
9087 * @class Roo.bootstrap.TableBody
9088 * @extends Roo.bootstrap.Component
9089 * Bootstrap TableBody class
9090 * @cfg {String} cls element class
9091 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092 * @cfg {String} align Aligns the content inside the element
9093 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9097 * Create a new TableBody
9098 * @param {Object} config The config object
9101 Roo.bootstrap.TableBody = function(config){
9102 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9113 getAutoCreate : function(){
9114 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9128 cfg.align = this.align;
9131 cfg.charoff = this.charoff;
9134 cfg.valign = this.valign;
9141 // initEvents : function()
9148 // this.store = Roo.factory(this.store, Roo.data);
9149 // this.store.on('load', this.onLoad, this);
9151 // this.store.load();
9155 // onLoad: function ()
9157 // this.fireEvent('load', this);
9167 * Ext JS Library 1.1.1
9168 * Copyright(c) 2006-2007, Ext JS, LLC.
9170 * Originally Released Under LGPL - original licence link has changed is not relivant.
9173 * <script type="text/javascript">
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9179 * @class Roo.form.Action
9180 * Internal Class used to handle form actions
9182 * @param {Roo.form.BasicForm} el The form element or its id
9183 * @param {Object} config Configuration options
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9191 this.options = options || {};
9194 * Client Validation Failed
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9199 * Server Validation Failed
9202 Roo.form.Action.SERVER_INVALID = 'server';
9204 * Connect to Server Failed
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9209 * Reading Data from Server Failed
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9214 Roo.form.Action.prototype = {
9216 failureType : undefined,
9217 response : undefined,
9221 run : function(options){
9226 success : function(response){
9231 handleResponse : function(response){
9235 // default connection failure
9236 failure : function(response){
9238 this.response = response;
9239 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240 this.form.afterAction(this, false);
9243 processResponse : function(response){
9244 this.response = response;
9245 if(!response.responseText){
9248 this.result = this.handleResponse(response);
9252 // utility functions used internally
9253 getUrl : function(appendParams){
9254 var url = this.options.url || this.form.url || this.form.el.dom.action;
9256 var p = this.getParams();
9258 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9264 getMethod : function(){
9265 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9268 getParams : function(){
9269 var bp = this.form.baseParams;
9270 var p = this.options.params;
9272 if(typeof p == "object"){
9273 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274 }else if(typeof p == 'string' && bp){
9275 p += '&' + Roo.urlEncode(bp);
9278 p = Roo.urlEncode(bp);
9283 createCallback : function(){
9285 success: this.success,
9286 failure: this.failure,
9288 timeout: (this.form.timeout*1000),
9289 upload: this.form.fileUpload ? this.success : undefined
9294 Roo.form.Action.Submit = function(form, options){
9295 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9301 haveProgress : false,
9302 uploadComplete : false,
9304 // uploadProgress indicator.
9305 uploadProgress : function()
9307 if (!this.form.progressUrl) {
9311 if (!this.haveProgress) {
9312 Roo.MessageBox.progress("Uploading", "Uploading");
9314 if (this.uploadComplete) {
9315 Roo.MessageBox.hide();
9319 this.haveProgress = true;
9321 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9323 var c = new Roo.data.Connection();
9325 url : this.form.progressUrl,
9330 success : function(req){
9331 //console.log(data);
9335 rdata = Roo.decode(req.responseText)
9337 Roo.log("Invalid data from server..");
9341 if (!rdata || !rdata.success) {
9343 Roo.MessageBox.alert(Roo.encode(rdata));
9346 var data = rdata.data;
9348 if (this.uploadComplete) {
9349 Roo.MessageBox.hide();
9354 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9358 this.uploadProgress.defer(2000,this);
9361 failure: function(data) {
9362 Roo.log('progress url failed ');
9373 // run get Values on the form, so it syncs any secondary forms.
9374 this.form.getValues();
9376 var o = this.options;
9377 var method = this.getMethod();
9378 var isPost = method == 'POST';
9379 if(o.clientValidation === false || this.form.isValid()){
9381 if (this.form.progressUrl) {
9382 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383 (new Date() * 1) + '' + Math.random());
9388 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389 form:this.form.el.dom,
9390 url:this.getUrl(!isPost),
9392 params:isPost ? this.getParams() : null,
9393 isUpload: this.form.fileUpload,
9394 formData : this.form.formData
9397 this.uploadProgress();
9399 }else if (o.clientValidation !== false){ // client validation failed
9400 this.failureType = Roo.form.Action.CLIENT_INVALID;
9401 this.form.afterAction(this, false);
9405 success : function(response)
9407 this.uploadComplete= true;
9408 if (this.haveProgress) {
9409 Roo.MessageBox.hide();
9413 var result = this.processResponse(response);
9414 if(result === true || result.success){
9415 this.form.afterAction(this, true);
9419 this.form.markInvalid(result.errors);
9420 this.failureType = Roo.form.Action.SERVER_INVALID;
9422 this.form.afterAction(this, false);
9424 failure : function(response)
9426 this.uploadComplete= true;
9427 if (this.haveProgress) {
9428 Roo.MessageBox.hide();
9431 this.response = response;
9432 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433 this.form.afterAction(this, false);
9436 handleResponse : function(response){
9437 if(this.form.errorReader){
9438 var rs = this.form.errorReader.read(response);
9441 for(var i = 0, len = rs.records.length; i < len; i++) {
9442 var r = rs.records[i];
9446 if(errors.length < 1){
9450 success : rs.success,
9456 ret = Roo.decode(response.responseText);
9460 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9470 Roo.form.Action.Load = function(form, options){
9471 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472 this.reader = this.form.reader;
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9480 Roo.Ajax.request(Roo.apply(
9481 this.createCallback(), {
9482 method:this.getMethod(),
9483 url:this.getUrl(false),
9484 params:this.getParams()
9488 success : function(response){
9490 var result = this.processResponse(response);
9491 if(result === true || !result.success || !result.data){
9492 this.failureType = Roo.form.Action.LOAD_FAILURE;
9493 this.form.afterAction(this, false);
9496 this.form.clearInvalid();
9497 this.form.setValues(result.data);
9498 this.form.afterAction(this, true);
9501 handleResponse : function(response){
9502 if(this.form.reader){
9503 var rs = this.form.reader.read(response);
9504 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9506 success : rs.success,
9510 return Roo.decode(response.responseText);
9514 Roo.form.Action.ACTION_TYPES = {
9515 'load' : Roo.form.Action.Load,
9516 'submit' : Roo.form.Action.Submit
9525 * @class Roo.bootstrap.Form
9526 * @extends Roo.bootstrap.Component
9527 * Bootstrap Form class
9528 * @cfg {String} method GET | POST (default POST)
9529 * @cfg {String} labelAlign top | left (default top)
9530 * @cfg {String} align left | right - for navbars
9531 * @cfg {Boolean} loadMask load mask when submit (default true)
9536 * @param {Object} config The config object
9540 Roo.bootstrap.Form = function(config){
9542 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9544 Roo.bootstrap.Form.popover.apply();
9548 * @event clientvalidation
9549 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550 * @param {Form} this
9551 * @param {Boolean} valid true if the form has passed client-side validation
9553 clientvalidation: true,
9555 * @event beforeaction
9556 * Fires before any action is performed. Return false to cancel the action.
9557 * @param {Form} this
9558 * @param {Action} action The action to be performed
9562 * @event actionfailed
9563 * Fires when an action fails.
9564 * @param {Form} this
9565 * @param {Action} action The action that failed
9567 actionfailed : true,
9569 * @event actioncomplete
9570 * Fires when an action is completed.
9571 * @param {Form} this
9572 * @param {Action} action The action that completed
9574 actioncomplete : true
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9581 * @cfg {String} method
9582 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9587 * The URL to use for form actions if one isn't supplied in the action options.
9590 * @cfg {Boolean} fileUpload
9591 * Set to true if this form is a file upload.
9595 * @cfg {Object} baseParams
9596 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9600 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9604 * @cfg {Sting} align (left|right) for navbar forms
9609 activeAction : null,
9612 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613 * element by passing it or its id or mask the form itself by passing in true.
9616 waitMsgTarget : false,
9621 * @cfg {Boolean} errorMask (true|false) default false
9626 * @cfg {Number} maskOffset Default 100
9631 * @cfg {Boolean} maskBody
9635 getAutoCreate : function(){
9639 method : this.method || 'POST',
9640 id : this.id || Roo.id(),
9643 if (this.parent().xtype.match(/^Nav/)) {
9644 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9648 if (this.labelAlign == 'left' ) {
9649 cfg.cls += ' form-horizontal';
9655 initEvents : function()
9657 this.el.on('submit', this.onSubmit, this);
9658 // this was added as random key presses on the form where triggering form submit.
9659 this.el.on('keypress', function(e) {
9660 if (e.getCharCode() != 13) {
9663 // we might need to allow it for textareas.. and some other items.
9664 // check e.getTarget().
9666 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9670 Roo.log("keypress blocked");
9678 onSubmit : function(e){
9683 * Returns true if client-side validation on the form is successful.
9686 isValid : function(){
9687 var items = this.getItems();
9691 items.each(function(f){
9697 Roo.log('invalid field: ' + f.name);
9701 if(!target && f.el.isVisible(true)){
9707 if(this.errorMask && !valid){
9708 Roo.bootstrap.Form.popover.mask(this, target);
9715 * Returns true if any fields in this form have changed since their original load.
9718 isDirty : function(){
9720 var items = this.getItems();
9721 items.each(function(f){
9731 * Performs a predefined action (submit or load) or custom actions you define on this form.
9732 * @param {String} actionName The name of the action type
9733 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9734 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735 * accept other config options):
9737 Property Type Description
9738 ---------------- --------------- ----------------------------------------------------------------------------------
9739 url String The url for the action (defaults to the form's url)
9740 method String The form method to use (defaults to the form's method, or POST if not defined)
9741 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9743 validate the form on the client (defaults to false)
9745 * @return {BasicForm} this
9747 doAction : function(action, options){
9748 if(typeof action == 'string'){
9749 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9751 if(this.fireEvent('beforeaction', this, action) !== false){
9752 this.beforeAction(action);
9753 action.run.defer(100, action);
9759 beforeAction : function(action){
9760 var o = action.options;
9765 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9767 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9770 // not really supported yet.. ??
9772 //if(this.waitMsgTarget === true){
9773 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774 //}else if(this.waitMsgTarget){
9775 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9778 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9784 afterAction : function(action, success){
9785 this.activeAction = null;
9786 var o = action.options;
9791 Roo.get(document.body).unmask();
9797 //if(this.waitMsgTarget === true){
9798 // this.el.unmask();
9799 //}else if(this.waitMsgTarget){
9800 // this.waitMsgTarget.unmask();
9802 // Roo.MessageBox.updateProgress(1);
9803 // Roo.MessageBox.hide();
9810 Roo.callback(o.success, o.scope, [this, action]);
9811 this.fireEvent('actioncomplete', this, action);
9815 // failure condition..
9816 // we have a scenario where updates need confirming.
9817 // eg. if a locking scenario exists..
9818 // we look for { errors : { needs_confirm : true }} in the response.
9820 (typeof(action.result) != 'undefined') &&
9821 (typeof(action.result.errors) != 'undefined') &&
9822 (typeof(action.result.errors.needs_confirm) != 'undefined')
9825 Roo.log("not supported yet");
9828 Roo.MessageBox.confirm(
9829 "Change requires confirmation",
9830 action.result.errorMsg,
9835 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9845 Roo.callback(o.failure, o.scope, [this, action]);
9846 // show an error message if no failed handler is set..
9847 if (!this.hasListener('actionfailed')) {
9848 Roo.log("need to add dialog support");
9850 Roo.MessageBox.alert("Error",
9851 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852 action.result.errorMsg :
9853 "Saving Failed, please check your entries or try again"
9858 this.fireEvent('actionfailed', this, action);
9863 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864 * @param {String} id The value to search for
9867 findField : function(id){
9868 var items = this.getItems();
9869 var field = items.get(id);
9871 items.each(function(f){
9872 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9879 return field || null;
9882 * Mark fields in this form invalid in bulk.
9883 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884 * @return {BasicForm} this
9886 markInvalid : function(errors){
9887 if(errors instanceof Array){
9888 for(var i = 0, len = errors.length; i < len; i++){
9889 var fieldError = errors[i];
9890 var f = this.findField(fieldError.id);
9892 f.markInvalid(fieldError.msg);
9898 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899 field.markInvalid(errors[id]);
9903 //Roo.each(this.childForms || [], function (f) {
9904 // f.markInvalid(errors);
9911 * Set values for fields in this form in bulk.
9912 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913 * @return {BasicForm} this
9915 setValues : function(values){
9916 if(values instanceof Array){ // array of objects
9917 for(var i = 0, len = values.length; i < len; i++){
9919 var f = this.findField(v.id);
9921 f.setValue(v.value);
9922 if(this.trackResetOnLoad){
9923 f.originalValue = f.getValue();
9927 }else{ // object hash
9930 if(typeof values[id] != 'function' && (field = this.findField(id))){
9932 if (field.setFromData &&
9934 field.displayField &&
9935 // combos' with local stores can
9936 // be queried via setValue()
9937 // to set their value..
9938 (field.store && !field.store.isLocal)
9942 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944 field.setFromData(sd);
9946 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9948 field.setFromData(values);
9951 field.setValue(values[id]);
9955 if(this.trackResetOnLoad){
9956 field.originalValue = field.getValue();
9962 //Roo.each(this.childForms || [], function (f) {
9963 // f.setValues(values);
9970 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971 * they are returned as an array.
9972 * @param {Boolean} asString
9975 getValues : function(asString){
9976 //if (this.childForms) {
9977 // copy values from the child forms
9978 // Roo.each(this.childForms, function (f) {
9979 // this.setValues(f.getValues());
9985 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986 if(asString === true){
9989 return Roo.urlDecode(fs);
9993 * Returns the fields in this form as an object with key/value pairs.
9994 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9997 getFieldValues : function(with_hidden)
9999 var items = this.getItems();
10001 items.each(function(f){
10003 if (!f.getName()) {
10007 var v = f.getValue();
10009 if (f.inputType =='radio') {
10010 if (typeof(ret[f.getName()]) == 'undefined') {
10011 ret[f.getName()] = ''; // empty..
10014 if (!f.el.dom.checked) {
10018 v = f.el.dom.value;
10022 if(f.xtype == 'MoneyField'){
10023 ret[f.currencyName] = f.getCurrency();
10026 // not sure if this supported any more..
10027 if ((typeof(v) == 'object') && f.getRawValue) {
10028 v = f.getRawValue() ; // dates..
10030 // combo boxes where name != hiddenName...
10031 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032 ret[f.name] = f.getRawValue();
10034 ret[f.getName()] = v;
10041 * Clears all invalid messages in this form.
10042 * @return {BasicForm} this
10044 clearInvalid : function(){
10045 var items = this.getItems();
10047 items.each(function(f){
10055 * Resets this form.
10056 * @return {BasicForm} this
10058 reset : function(){
10059 var items = this.getItems();
10060 items.each(function(f){
10064 Roo.each(this.childForms || [], function (f) {
10072 getItems : function()
10074 var r=new Roo.util.MixedCollection(false, function(o){
10075 return o.id || (o.id = Roo.id());
10077 var iter = function(el) {
10084 Roo.each(el.items,function(e) {
10093 hideFields : function(items)
10095 Roo.each(items, function(i){
10097 var f = this.findField(i);
10108 showFields : function(items)
10110 Roo.each(items, function(i){
10112 var f = this.findField(i);
10125 Roo.apply(Roo.bootstrap.Form, {
10141 intervalID : false,
10147 if(this.isApplied){
10152 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10158 this.maskEl.top.enableDisplayMode("block");
10159 this.maskEl.left.enableDisplayMode("block");
10160 this.maskEl.bottom.enableDisplayMode("block");
10161 this.maskEl.right.enableDisplayMode("block");
10163 this.toolTip = new Roo.bootstrap.Tooltip({
10164 cls : 'roo-form-error-popover',
10166 'left' : ['r-l', [-2,0], 'right'],
10167 'right' : ['l-r', [2,0], 'left'],
10168 'bottom' : ['tl-bl', [0,2], 'top'],
10169 'top' : [ 'bl-tl', [0,-2], 'bottom']
10173 this.toolTip.render(Roo.get(document.body));
10175 this.toolTip.el.enableDisplayMode("block");
10177 Roo.get(document.body).on('click', function(){
10181 Roo.get(document.body).on('touchstart', function(){
10185 this.isApplied = true
10188 mask : function(form, target)
10192 this.target = target;
10194 if(!this.form.errorMask || !target.el){
10198 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10200 Roo.log(scrollable);
10202 var ot = this.target.el.calcOffsetsTo(scrollable);
10204 var scrollTo = ot[1] - this.form.maskOffset;
10206 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10208 scrollable.scrollTo('top', scrollTo);
10210 var box = this.target.el.getBox();
10212 var zIndex = Roo.bootstrap.Modal.zIndex++;
10215 this.maskEl.top.setStyle('position', 'absolute');
10216 this.maskEl.top.setStyle('z-index', zIndex);
10217 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218 this.maskEl.top.setLeft(0);
10219 this.maskEl.top.setTop(0);
10220 this.maskEl.top.show();
10222 this.maskEl.left.setStyle('position', 'absolute');
10223 this.maskEl.left.setStyle('z-index', zIndex);
10224 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225 this.maskEl.left.setLeft(0);
10226 this.maskEl.left.setTop(box.y - this.padding);
10227 this.maskEl.left.show();
10229 this.maskEl.bottom.setStyle('position', 'absolute');
10230 this.maskEl.bottom.setStyle('z-index', zIndex);
10231 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232 this.maskEl.bottom.setLeft(0);
10233 this.maskEl.bottom.setTop(box.bottom + this.padding);
10234 this.maskEl.bottom.show();
10236 this.maskEl.right.setStyle('position', 'absolute');
10237 this.maskEl.right.setStyle('z-index', zIndex);
10238 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239 this.maskEl.right.setLeft(box.right + this.padding);
10240 this.maskEl.right.setTop(box.y - this.padding);
10241 this.maskEl.right.show();
10243 this.toolTip.bindEl = this.target.el;
10245 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10247 var tip = this.target.blankText;
10249 if(this.target.getValue() !== '' ) {
10251 if (this.target.invalidText.length) {
10252 tip = this.target.invalidText;
10253 } else if (this.target.regexText.length){
10254 tip = this.target.regexText;
10258 this.toolTip.show(tip);
10260 this.intervalID = window.setInterval(function() {
10261 Roo.bootstrap.Form.popover.unmask();
10264 window.onwheel = function(){ return false;};
10266 (function(){ this.isMasked = true; }).defer(500, this);
10270 unmask : function()
10272 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10276 this.maskEl.top.setStyle('position', 'absolute');
10277 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278 this.maskEl.top.hide();
10280 this.maskEl.left.setStyle('position', 'absolute');
10281 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282 this.maskEl.left.hide();
10284 this.maskEl.bottom.setStyle('position', 'absolute');
10285 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286 this.maskEl.bottom.hide();
10288 this.maskEl.right.setStyle('position', 'absolute');
10289 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290 this.maskEl.right.hide();
10292 this.toolTip.hide();
10294 this.toolTip.el.hide();
10296 window.onwheel = function(){ return true;};
10298 if(this.intervalID){
10299 window.clearInterval(this.intervalID);
10300 this.intervalID = false;
10303 this.isMasked = false;
10313 * Ext JS Library 1.1.1
10314 * Copyright(c) 2006-2007, Ext JS, LLC.
10316 * Originally Released Under LGPL - original licence link has changed is not relivant.
10319 * <script type="text/javascript">
10322 * @class Roo.form.VTypes
10323 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10326 Roo.form.VTypes = function(){
10327 // closure these in so they are only created once.
10328 var alpha = /^[a-zA-Z_]+$/;
10329 var alphanum = /^[a-zA-Z0-9_]+$/;
10330 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10333 // All these messages and functions are configurable
10336 * The function used to validate email addresses
10337 * @param {String} value The email address
10339 'email' : function(v){
10340 return email.test(v);
10343 * The error text to display when the email validation function returns false
10346 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10348 * The keystroke filter mask to be applied on email input
10351 'emailMask' : /[a-z0-9_\.\-@]/i,
10354 * The function used to validate URLs
10355 * @param {String} value The URL
10357 'url' : function(v){
10358 return url.test(v);
10361 * The error text to display when the url validation function returns false
10364 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10367 * The function used to validate alpha values
10368 * @param {String} value The value
10370 'alpha' : function(v){
10371 return alpha.test(v);
10374 * The error text to display when the alpha validation function returns false
10377 'alphaText' : 'This field should only contain letters and _',
10379 * The keystroke filter mask to be applied on alpha input
10382 'alphaMask' : /[a-z_]/i,
10385 * The function used to validate alphanumeric values
10386 * @param {String} value The value
10388 'alphanum' : function(v){
10389 return alphanum.test(v);
10392 * The error text to display when the alphanumeric validation function returns false
10395 'alphanumText' : 'This field should only contain letters, numbers and _',
10397 * The keystroke filter mask to be applied on alphanumeric input
10400 'alphanumMask' : /[a-z0-9_]/i
10410 * @class Roo.bootstrap.Input
10411 * @extends Roo.bootstrap.Component
10412 * Bootstrap Input class
10413 * @cfg {Boolean} disabled is it disabled
10414 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10415 * @cfg {String} name name of the input
10416 * @cfg {string} fieldLabel - the label associated
10417 * @cfg {string} placeholder - placeholder to put in text.
10418 * @cfg {string} before - input group add on before
10419 * @cfg {string} after - input group add on after
10420 * @cfg {string} size - (lg|sm) or leave empty..
10421 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423 * @cfg {Number} md colspan out of 12 for computer-sized screens
10424 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425 * @cfg {string} value default value of the input
10426 * @cfg {Number} labelWidth set the width of label
10427 * @cfg {Number} labellg set the width of label (1-12)
10428 * @cfg {Number} labelmd set the width of label (1-12)
10429 * @cfg {Number} labelsm set the width of label (1-12)
10430 * @cfg {Number} labelxs set the width of label (1-12)
10431 * @cfg {String} labelAlign (top|left)
10432 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434 * @cfg {String} indicatorpos (left|right) default left
10435 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10439 * @cfg {String} align (left|center|right) Default left
10440 * @cfg {Boolean} forceFeedback (true|false) Default false
10443 * Create a new Input
10444 * @param {Object} config The config object
10447 Roo.bootstrap.Input = function(config){
10449 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10454 * Fires when this field receives input focus.
10455 * @param {Roo.form.Field} this
10460 * Fires when this field loses input focus.
10461 * @param {Roo.form.Field} this
10465 * @event specialkey
10466 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10467 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468 * @param {Roo.form.Field} this
10469 * @param {Roo.EventObject} e The event object
10474 * Fires just before the field blurs if the field value has changed.
10475 * @param {Roo.form.Field} this
10476 * @param {Mixed} newValue The new value
10477 * @param {Mixed} oldValue The original value
10482 * Fires after the field has been marked as invalid.
10483 * @param {Roo.form.Field} this
10484 * @param {String} msg The validation message
10489 * Fires after the field has been validated with no errors.
10490 * @param {Roo.form.Field} this
10495 * Fires after the key up
10496 * @param {Roo.form.Field} this
10497 * @param {Roo.EventObject} e The event Object
10503 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10505 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10506 automatic validation (defaults to "keyup").
10508 validationEvent : "keyup",
10510 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10512 validateOnBlur : true,
10514 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10516 validationDelay : 250,
10518 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10520 focusClass : "x-form-focus", // not needed???
10524 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10526 invalidClass : "has-warning",
10529 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10531 validClass : "has-success",
10534 * @cfg {Boolean} hasFeedback (true|false) default true
10536 hasFeedback : true,
10539 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10541 invalidFeedbackClass : "glyphicon-warning-sign",
10544 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10546 validFeedbackClass : "glyphicon-ok",
10549 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10551 selectOnFocus : false,
10554 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10558 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10563 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10565 disableKeyFilter : false,
10568 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10572 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10576 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10578 blankText : "Please complete this mandatory field",
10581 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10585 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10587 maxLength : Number.MAX_VALUE,
10589 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10591 minLengthText : "The minimum length for this field is {0}",
10593 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10595 maxLengthText : "The maximum length for this field is {0}",
10599 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10600 * If available, this function will be called only after the basic validators all return true, and will be passed the
10601 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10605 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10606 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10607 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10611 * @cfg {String} regexText -- Depricated - use Invalid Text
10616 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10622 autocomplete: false,
10626 inputType : 'text',
10629 placeholder: false,
10634 preventMark: false,
10635 isFormField : true,
10638 labelAlign : false,
10641 formatedValue : false,
10642 forceFeedback : false,
10644 indicatorpos : 'left',
10654 parentLabelAlign : function()
10657 while (parent.parent()) {
10658 parent = parent.parent();
10659 if (typeof(parent.labelAlign) !='undefined') {
10660 return parent.labelAlign;
10667 getAutoCreate : function()
10669 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10675 if(this.inputType != 'hidden'){
10676 cfg.cls = 'form-group' //input-group
10682 type : this.inputType,
10683 value : this.value,
10684 cls : 'form-control',
10685 placeholder : this.placeholder || '',
10686 autocomplete : this.autocomplete || 'new-password'
10688 if (this.inputType == 'file') {
10689 input.style = 'overflow:hidden'; // why not in CSS?
10692 if(this.capture.length){
10693 input.capture = this.capture;
10696 if(this.accept.length){
10697 input.accept = this.accept + "/*";
10701 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10704 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10705 input.maxLength = this.maxLength;
10708 if (this.disabled) {
10709 input.disabled=true;
10712 if (this.readOnly) {
10713 input.readonly=true;
10717 input.name = this.name;
10721 input.cls += ' input-' + this.size;
10725 ['xs','sm','md','lg'].map(function(size){
10726 if (settings[size]) {
10727 cfg.cls += ' col-' + size + '-' + settings[size];
10731 var inputblock = input;
10735 cls: 'glyphicon form-control-feedback'
10738 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10741 cls : 'has-feedback',
10749 if (this.before || this.after) {
10752 cls : 'input-group',
10756 if (this.before && typeof(this.before) == 'string') {
10758 inputblock.cn.push({
10760 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10764 if (this.before && typeof(this.before) == 'object') {
10765 this.before = Roo.factory(this.before);
10767 inputblock.cn.push({
10769 cls : 'roo-input-before input-group-prepend input-group-' +
10770 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10774 inputblock.cn.push(input);
10776 if (this.after && typeof(this.after) == 'string') {
10777 inputblock.cn.push({
10779 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10783 if (this.after && typeof(this.after) == 'object') {
10784 this.after = Roo.factory(this.after);
10786 inputblock.cn.push({
10788 cls : 'roo-input-after input-group-append input-group-' +
10789 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10793 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794 inputblock.cls += ' has-feedback';
10795 inputblock.cn.push(feedback);
10800 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10801 tooltip : 'This field is required'
10803 if (this.allowBlank ) {
10804 indicator.style = this.allowBlank ? ' display:none' : '';
10806 if (align ==='left' && this.fieldLabel.length) {
10808 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10815 cls : 'control-label col-form-label',
10816 html : this.fieldLabel
10827 var labelCfg = cfg.cn[1];
10828 var contentCfg = cfg.cn[2];
10830 if(this.indicatorpos == 'right'){
10835 cls : 'control-label col-form-label',
10839 html : this.fieldLabel
10853 labelCfg = cfg.cn[0];
10854 contentCfg = cfg.cn[1];
10858 if(this.labelWidth > 12){
10859 labelCfg.style = "width: " + this.labelWidth + 'px';
10862 if(this.labelWidth < 13 && this.labelmd == 0){
10863 this.labelmd = this.labelWidth;
10866 if(this.labellg > 0){
10867 labelCfg.cls += ' col-lg-' + this.labellg;
10868 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10871 if(this.labelmd > 0){
10872 labelCfg.cls += ' col-md-' + this.labelmd;
10873 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10876 if(this.labelsm > 0){
10877 labelCfg.cls += ' col-sm-' + this.labelsm;
10878 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10881 if(this.labelxs > 0){
10882 labelCfg.cls += ' col-xs-' + this.labelxs;
10883 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10887 } else if ( this.fieldLabel.length) {
10894 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10895 tooltip : 'This field is required',
10896 style : this.allowBlank ? ' display:none' : ''
10900 //cls : 'input-group-addon',
10901 html : this.fieldLabel
10909 if(this.indicatorpos == 'right'){
10914 //cls : 'input-group-addon',
10915 html : this.fieldLabel
10920 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10921 tooltip : 'This field is required',
10922 style : this.allowBlank ? ' display:none' : ''
10942 if (this.parentType === 'Navbar' && this.parent().bar) {
10943 cfg.cls += ' navbar-form';
10946 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10947 // on BS4 we do this only if not form
10948 cfg.cls += ' navbar-form';
10956 * return the real input element.
10958 inputEl: function ()
10960 return this.el.select('input.form-control',true).first();
10963 tooltipEl : function()
10965 return this.inputEl();
10968 indicatorEl : function()
10970 if (Roo.bootstrap.version == 4) {
10971 return false; // not enabled in v4 yet.
10974 var indicator = this.el.select('i.roo-required-indicator',true).first();
10984 setDisabled : function(v)
10986 var i = this.inputEl().dom;
10988 i.removeAttribute('disabled');
10992 i.setAttribute('disabled','true');
10994 initEvents : function()
10997 this.inputEl().on("keydown" , this.fireKey, this);
10998 this.inputEl().on("focus", this.onFocus, this);
10999 this.inputEl().on("blur", this.onBlur, this);
11001 this.inputEl().relayEvent('keyup', this);
11003 this.indicator = this.indicatorEl();
11005 if(this.indicator){
11006 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11009 // reference to original value for reset
11010 this.originalValue = this.getValue();
11011 //Roo.form.TextField.superclass.initEvents.call(this);
11012 if(this.validationEvent == 'keyup'){
11013 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11014 this.inputEl().on('keyup', this.filterValidation, this);
11016 else if(this.validationEvent !== false){
11017 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11020 if(this.selectOnFocus){
11021 this.on("focus", this.preFocus, this);
11024 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11025 this.inputEl().on("keypress", this.filterKeys, this);
11027 this.inputEl().relayEvent('keypress', this);
11030 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11031 this.el.on("click", this.autoSize, this);
11034 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11035 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11038 if (typeof(this.before) == 'object') {
11039 this.before.render(this.el.select('.roo-input-before',true).first());
11041 if (typeof(this.after) == 'object') {
11042 this.after.render(this.el.select('.roo-input-after',true).first());
11045 this.inputEl().on('change', this.onChange, this);
11048 filterValidation : function(e){
11049 if(!e.isNavKeyPress()){
11050 this.validationTask.delay(this.validationDelay);
11054 * Validates the field value
11055 * @return {Boolean} True if the value is valid, else false
11057 validate : function(){
11058 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11059 if(this.disabled || this.validateValue(this.getRawValue())){
11064 this.markInvalid();
11070 * Validates a value according to the field's validation rules and marks the field as invalid
11071 * if the validation fails
11072 * @param {Mixed} value The value to validate
11073 * @return {Boolean} True if the value is valid, else false
11075 validateValue : function(value)
11077 if(this.getVisibilityEl().hasClass('hidden')){
11081 if(value.length < 1) { // if it's blank
11082 if(this.allowBlank){
11088 if(value.length < this.minLength){
11091 if(value.length > this.maxLength){
11095 var vt = Roo.form.VTypes;
11096 if(!vt[this.vtype](value, this)){
11100 if(typeof this.validator == "function"){
11101 var msg = this.validator(value);
11105 if (typeof(msg) == 'string') {
11106 this.invalidText = msg;
11110 if(this.regex && !this.regex.test(value)){
11118 fireKey : function(e){
11119 //Roo.log('field ' + e.getKey());
11120 if(e.isNavKeyPress()){
11121 this.fireEvent("specialkey", this, e);
11124 focus : function (selectText){
11126 this.inputEl().focus();
11127 if(selectText === true){
11128 this.inputEl().dom.select();
11134 onFocus : function(){
11135 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136 // this.el.addClass(this.focusClass);
11138 if(!this.hasFocus){
11139 this.hasFocus = true;
11140 this.startValue = this.getValue();
11141 this.fireEvent("focus", this);
11145 beforeBlur : Roo.emptyFn,
11149 onBlur : function(){
11151 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11152 //this.el.removeClass(this.focusClass);
11154 this.hasFocus = false;
11155 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11158 var v = this.getValue();
11159 if(String(v) !== String(this.startValue)){
11160 this.fireEvent('change', this, v, this.startValue);
11162 this.fireEvent("blur", this);
11165 onChange : function(e)
11167 var v = this.getValue();
11168 if(String(v) !== String(this.startValue)){
11169 this.fireEvent('change', this, v, this.startValue);
11175 * Resets the current field value to the originally loaded value and clears any validation messages
11177 reset : function(){
11178 this.setValue(this.originalValue);
11182 * Returns the name of the field
11183 * @return {Mixed} name The name field
11185 getName: function(){
11189 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11190 * @return {Mixed} value The field value
11192 getValue : function(){
11194 var v = this.inputEl().getValue();
11199 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11200 * @return {Mixed} value The field value
11202 getRawValue : function(){
11203 var v = this.inputEl().getValue();
11209 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11210 * @param {Mixed} value The value to set
11212 setRawValue : function(v){
11213 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11216 selectText : function(start, end){
11217 var v = this.getRawValue();
11219 start = start === undefined ? 0 : start;
11220 end = end === undefined ? v.length : end;
11221 var d = this.inputEl().dom;
11222 if(d.setSelectionRange){
11223 d.setSelectionRange(start, end);
11224 }else if(d.createTextRange){
11225 var range = d.createTextRange();
11226 range.moveStart("character", start);
11227 range.moveEnd("character", v.length-end);
11234 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11235 * @param {Mixed} value The value to set
11237 setValue : function(v){
11240 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11246 processValue : function(value){
11247 if(this.stripCharsRe){
11248 var newValue = value.replace(this.stripCharsRe, '');
11249 if(newValue !== value){
11250 this.setRawValue(newValue);
11257 preFocus : function(){
11259 if(this.selectOnFocus){
11260 this.inputEl().dom.select();
11263 filterKeys : function(e){
11264 var k = e.getKey();
11265 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11268 var c = e.getCharCode(), cc = String.fromCharCode(c);
11269 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11272 if(!this.maskRe.test(cc)){
11277 * Clear any invalid styles/messages for this field
11279 clearInvalid : function(){
11281 if(!this.el || this.preventMark){ // not rendered
11286 this.el.removeClass([this.invalidClass, 'is-invalid']);
11288 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11290 var feedback = this.el.select('.form-control-feedback', true).first();
11293 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11298 if(this.indicator){
11299 this.indicator.removeClass('visible');
11300 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11303 this.fireEvent('valid', this);
11307 * Mark this field as valid
11309 markValid : function()
11311 if(!this.el || this.preventMark){ // not rendered...
11315 this.el.removeClass([this.invalidClass, this.validClass]);
11316 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11318 var feedback = this.el.select('.form-control-feedback', true).first();
11321 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11324 if(this.indicator){
11325 this.indicator.removeClass('visible');
11326 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11334 if(this.allowBlank && !this.getRawValue().length){
11337 if (Roo.bootstrap.version == 3) {
11338 this.el.addClass(this.validClass);
11340 this.inputEl().addClass('is-valid');
11343 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11345 var feedback = this.el.select('.form-control-feedback', true).first();
11348 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11349 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11354 this.fireEvent('valid', this);
11358 * Mark this field as invalid
11359 * @param {String} msg The validation message
11361 markInvalid : function(msg)
11363 if(!this.el || this.preventMark){ // not rendered
11367 this.el.removeClass([this.invalidClass, this.validClass]);
11368 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11370 var feedback = this.el.select('.form-control-feedback', true).first();
11373 this.el.select('.form-control-feedback', true).first().removeClass(
11374 [this.invalidFeedbackClass, this.validFeedbackClass]);
11381 if(this.allowBlank && !this.getRawValue().length){
11385 if(this.indicator){
11386 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11387 this.indicator.addClass('visible');
11389 if (Roo.bootstrap.version == 3) {
11390 this.el.addClass(this.invalidClass);
11392 this.inputEl().addClass('is-invalid');
11397 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11399 var feedback = this.el.select('.form-control-feedback', true).first();
11402 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11404 if(this.getValue().length || this.forceFeedback){
11405 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11412 this.fireEvent('invalid', this, msg);
11415 SafariOnKeyDown : function(event)
11417 // this is a workaround for a password hang bug on chrome/ webkit.
11418 if (this.inputEl().dom.type != 'password') {
11422 var isSelectAll = false;
11424 if(this.inputEl().dom.selectionEnd > 0){
11425 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11427 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11428 event.preventDefault();
11433 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11435 event.preventDefault();
11436 // this is very hacky as keydown always get's upper case.
11438 var cc = String.fromCharCode(event.getCharCode());
11439 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11443 adjustWidth : function(tag, w){
11444 tag = tag.toLowerCase();
11445 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11446 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11447 if(tag == 'input'){
11450 if(tag == 'textarea'){
11453 }else if(Roo.isOpera){
11454 if(tag == 'input'){
11457 if(tag == 'textarea'){
11465 setFieldLabel : function(v)
11467 if(!this.rendered){
11471 if(this.indicatorEl()){
11472 var ar = this.el.select('label > span',true);
11474 if (ar.elements.length) {
11475 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476 this.fieldLabel = v;
11480 var br = this.el.select('label',true);
11482 if(br.elements.length) {
11483 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484 this.fieldLabel = v;
11488 Roo.log('Cannot Found any of label > span || label in input');
11492 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11493 this.fieldLabel = v;
11508 * @class Roo.bootstrap.TextArea
11509 * @extends Roo.bootstrap.Input
11510 * Bootstrap TextArea class
11511 * @cfg {Number} cols Specifies the visible width of a text area
11512 * @cfg {Number} rows Specifies the visible number of lines in a text area
11513 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11514 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11515 * @cfg {string} html text
11518 * Create a new TextArea
11519 * @param {Object} config The config object
11522 Roo.bootstrap.TextArea = function(config){
11523 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11537 getAutoCreate : function(){
11539 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11545 if(this.inputType != 'hidden'){
11546 cfg.cls = 'form-group' //input-group
11554 value : this.value || '',
11555 html: this.html || '',
11556 cls : 'form-control',
11557 placeholder : this.placeholder || ''
11561 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11562 input.maxLength = this.maxLength;
11566 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11570 input.cols = this.cols;
11573 if (this.readOnly) {
11574 input.readonly = true;
11578 input.name = this.name;
11582 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11586 ['xs','sm','md','lg'].map(function(size){
11587 if (settings[size]) {
11588 cfg.cls += ' col-' + size + '-' + settings[size];
11592 var inputblock = input;
11594 if(this.hasFeedback && !this.allowBlank){
11598 cls: 'glyphicon form-control-feedback'
11602 cls : 'has-feedback',
11611 if (this.before || this.after) {
11614 cls : 'input-group',
11618 inputblock.cn.push({
11620 cls : 'input-group-addon',
11625 inputblock.cn.push(input);
11627 if(this.hasFeedback && !this.allowBlank){
11628 inputblock.cls += ' has-feedback';
11629 inputblock.cn.push(feedback);
11633 inputblock.cn.push({
11635 cls : 'input-group-addon',
11642 if (align ==='left' && this.fieldLabel.length) {
11647 cls : 'control-label',
11648 html : this.fieldLabel
11659 if(this.labelWidth > 12){
11660 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11663 if(this.labelWidth < 13 && this.labelmd == 0){
11664 this.labelmd = this.labelWidth;
11667 if(this.labellg > 0){
11668 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11669 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11672 if(this.labelmd > 0){
11673 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11674 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11677 if(this.labelsm > 0){
11678 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11679 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11682 if(this.labelxs > 0){
11683 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11684 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11687 } else if ( this.fieldLabel.length) {
11692 //cls : 'input-group-addon',
11693 html : this.fieldLabel
11711 if (this.disabled) {
11712 input.disabled=true;
11719 * return the real textarea element.
11721 inputEl: function ()
11723 return this.el.select('textarea.form-control',true).first();
11727 * Clear any invalid styles/messages for this field
11729 clearInvalid : function()
11732 if(!this.el || this.preventMark){ // not rendered
11736 var label = this.el.select('label', true).first();
11737 var icon = this.el.select('i.fa-star', true).first();
11742 this.el.removeClass( this.validClass);
11743 this.inputEl().removeClass('is-invalid');
11745 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11747 var feedback = this.el.select('.form-control-feedback', true).first();
11750 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11755 this.fireEvent('valid', this);
11759 * Mark this field as valid
11761 markValid : function()
11763 if(!this.el || this.preventMark){ // not rendered
11767 this.el.removeClass([this.invalidClass, this.validClass]);
11768 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11770 var feedback = this.el.select('.form-control-feedback', true).first();
11773 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11776 if(this.disabled || this.allowBlank){
11780 var label = this.el.select('label', true).first();
11781 var icon = this.el.select('i.fa-star', true).first();
11786 if (Roo.bootstrap.version == 3) {
11787 this.el.addClass(this.validClass);
11789 this.inputEl().addClass('is-valid');
11793 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11795 var feedback = this.el.select('.form-control-feedback', true).first();
11798 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11799 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11804 this.fireEvent('valid', this);
11808 * Mark this field as invalid
11809 * @param {String} msg The validation message
11811 markInvalid : function(msg)
11813 if(!this.el || this.preventMark){ // not rendered
11817 this.el.removeClass([this.invalidClass, this.validClass]);
11818 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11820 var feedback = this.el.select('.form-control-feedback', true).first();
11823 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11826 if(this.disabled || this.allowBlank){
11830 var label = this.el.select('label', true).first();
11831 var icon = this.el.select('i.fa-star', true).first();
11833 if(!this.getValue().length && label && !icon){
11834 this.el.createChild({
11836 cls : 'text-danger fa fa-lg fa-star',
11837 tooltip : 'This field is required',
11838 style : 'margin-right:5px;'
11842 if (Roo.bootstrap.version == 3) {
11843 this.el.addClass(this.invalidClass);
11845 this.inputEl().addClass('is-invalid');
11848 // fixme ... this may be depricated need to test..
11849 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11851 var feedback = this.el.select('.form-control-feedback', true).first();
11854 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11856 if(this.getValue().length || this.forceFeedback){
11857 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11864 this.fireEvent('invalid', this, msg);
11872 * trigger field - base class for combo..
11877 * @class Roo.bootstrap.TriggerField
11878 * @extends Roo.bootstrap.Input
11879 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11880 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11881 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11882 * for which you can provide a custom implementation. For example:
11884 var trigger = new Roo.bootstrap.TriggerField();
11885 trigger.onTriggerClick = myTriggerFn;
11886 trigger.applyTo('my-field');
11889 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11890 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11891 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11892 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11893 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11896 * Create a new TriggerField.
11897 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11898 * to the base TextField)
11900 Roo.bootstrap.TriggerField = function(config){
11901 this.mimicing = false;
11902 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11905 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11907 * @cfg {String} triggerClass A CSS class to apply to the trigger
11910 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11915 * @cfg {Boolean} removable (true|false) special filter default false
11919 /** @cfg {Boolean} grow @hide */
11920 /** @cfg {Number} growMin @hide */
11921 /** @cfg {Number} growMax @hide */
11927 autoSize: Roo.emptyFn,
11931 deferHeight : true,
11934 actionMode : 'wrap',
11939 getAutoCreate : function(){
11941 var align = this.labelAlign || this.parentLabelAlign();
11946 cls: 'form-group' //input-group
11953 type : this.inputType,
11954 cls : 'form-control',
11955 autocomplete: 'new-password',
11956 placeholder : this.placeholder || ''
11960 input.name = this.name;
11963 input.cls += ' input-' + this.size;
11966 if (this.disabled) {
11967 input.disabled=true;
11970 var inputblock = input;
11972 if(this.hasFeedback && !this.allowBlank){
11976 cls: 'glyphicon form-control-feedback'
11979 if(this.removable && !this.editable ){
11981 cls : 'has-feedback',
11987 cls : 'roo-combo-removable-btn close'
11994 cls : 'has-feedback',
12003 if(this.removable && !this.editable ){
12005 cls : 'roo-removable',
12011 cls : 'roo-combo-removable-btn close'
12018 if (this.before || this.after) {
12021 cls : 'input-group',
12025 inputblock.cn.push({
12027 cls : 'input-group-addon input-group-prepend input-group-text',
12032 inputblock.cn.push(input);
12034 if(this.hasFeedback && !this.allowBlank){
12035 inputblock.cls += ' has-feedback';
12036 inputblock.cn.push(feedback);
12040 inputblock.cn.push({
12042 cls : 'input-group-addon input-group-append input-group-text',
12051 var ibwrap = inputblock;
12056 cls: 'roo-select2-choices',
12060 cls: 'roo-select2-search-field',
12072 cls: 'roo-select2-container input-group',
12077 cls: 'form-hidden-field'
12083 if(!this.multiple && this.showToggleBtn){
12089 if (this.caret != false) {
12092 cls: 'fa fa-' + this.caret
12099 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12101 Roo.bootstrap.version == 3 ? caret : '',
12104 cls: 'combobox-clear',
12118 combobox.cls += ' roo-select2-container-multi';
12122 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12123 tooltip : 'This field is required'
12125 if (Roo.bootstrap.version == 4) {
12128 style : 'display:none'
12133 if (align ==='left' && this.fieldLabel.length) {
12135 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12142 cls : 'control-label',
12143 html : this.fieldLabel
12155 var labelCfg = cfg.cn[1];
12156 var contentCfg = cfg.cn[2];
12158 if(this.indicatorpos == 'right'){
12163 cls : 'control-label',
12167 html : this.fieldLabel
12181 labelCfg = cfg.cn[0];
12182 contentCfg = cfg.cn[1];
12185 if(this.labelWidth > 12){
12186 labelCfg.style = "width: " + this.labelWidth + 'px';
12189 if(this.labelWidth < 13 && this.labelmd == 0){
12190 this.labelmd = this.labelWidth;
12193 if(this.labellg > 0){
12194 labelCfg.cls += ' col-lg-' + this.labellg;
12195 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12198 if(this.labelmd > 0){
12199 labelCfg.cls += ' col-md-' + this.labelmd;
12200 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12203 if(this.labelsm > 0){
12204 labelCfg.cls += ' col-sm-' + this.labelsm;
12205 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12208 if(this.labelxs > 0){
12209 labelCfg.cls += ' col-xs-' + this.labelxs;
12210 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12213 } else if ( this.fieldLabel.length) {
12214 // Roo.log(" label");
12219 //cls : 'input-group-addon',
12220 html : this.fieldLabel
12228 if(this.indicatorpos == 'right'){
12236 html : this.fieldLabel
12250 // Roo.log(" no label && no align");
12257 ['xs','sm','md','lg'].map(function(size){
12258 if (settings[size]) {
12259 cfg.cls += ' col-' + size + '-' + settings[size];
12270 onResize : function(w, h){
12271 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12272 // if(typeof w == 'number'){
12273 // var x = w - this.trigger.getWidth();
12274 // this.inputEl().setWidth(this.adjustWidth('input', x));
12275 // this.trigger.setStyle('left', x+'px');
12280 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12283 getResizeEl : function(){
12284 return this.inputEl();
12288 getPositionEl : function(){
12289 return this.inputEl();
12293 alignErrorIcon : function(){
12294 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12298 initEvents : function(){
12302 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12303 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12304 if(!this.multiple && this.showToggleBtn){
12305 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12306 if(this.hideTrigger){
12307 this.trigger.setDisplayed(false);
12309 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12313 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12316 if(this.removable && !this.editable && !this.tickable){
12317 var close = this.closeTriggerEl();
12320 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12321 close.on('click', this.removeBtnClick, this, close);
12325 //this.trigger.addClassOnOver('x-form-trigger-over');
12326 //this.trigger.addClassOnClick('x-form-trigger-click');
12329 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12333 closeTriggerEl : function()
12335 var close = this.el.select('.roo-combo-removable-btn', true).first();
12336 return close ? close : false;
12339 removeBtnClick : function(e, h, el)
12341 e.preventDefault();
12343 if(this.fireEvent("remove", this) !== false){
12345 this.fireEvent("afterremove", this)
12349 createList : function()
12351 this.list = Roo.get(document.body).createChild({
12352 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12353 cls: 'typeahead typeahead-long dropdown-menu shadow',
12354 style: 'display:none'
12357 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12362 initTrigger : function(){
12367 onDestroy : function(){
12369 this.trigger.removeAllListeners();
12370 // this.trigger.remove();
12373 // this.wrap.remove();
12375 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12379 onFocus : function(){
12380 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12382 if(!this.mimicing){
12383 this.wrap.addClass('x-trigger-wrap-focus');
12384 this.mimicing = true;
12385 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12386 if(this.monitorTab){
12387 this.el.on("keydown", this.checkTab, this);
12394 checkTab : function(e){
12395 if(e.getKey() == e.TAB){
12396 this.triggerBlur();
12401 onBlur : function(){
12406 mimicBlur : function(e, t){
12408 if(!this.wrap.contains(t) && this.validateBlur()){
12409 this.triggerBlur();
12415 triggerBlur : function(){
12416 this.mimicing = false;
12417 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12418 if(this.monitorTab){
12419 this.el.un("keydown", this.checkTab, this);
12421 //this.wrap.removeClass('x-trigger-wrap-focus');
12422 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12426 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12427 validateBlur : function(e, t){
12432 onDisable : function(){
12433 this.inputEl().dom.disabled = true;
12434 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12436 // this.wrap.addClass('x-item-disabled');
12441 onEnable : function(){
12442 this.inputEl().dom.disabled = false;
12443 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12445 // this.el.removeClass('x-item-disabled');
12450 onShow : function(){
12451 var ae = this.getActionEl();
12454 ae.dom.style.display = '';
12455 ae.dom.style.visibility = 'visible';
12461 onHide : function(){
12462 var ae = this.getActionEl();
12463 ae.dom.style.display = 'none';
12467 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12468 * by an implementing function.
12470 * @param {EventObject} e
12472 onTriggerClick : Roo.emptyFn
12480 * @class Roo.bootstrap.CardUploader
12481 * @extends Roo.bootstrap.Button
12482 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12483 * @cfg {Number} errorTimeout default 3000
12484 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12485 * @cfg {Array} html The button text.
12489 * Create a new CardUploader
12490 * @param {Object} config The config object
12493 Roo.bootstrap.CardUploader = function(config){
12497 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12500 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12507 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12510 errorTimeout : 3000,
12514 fileCollection : false,
12517 getAutoCreate : function()
12521 cls :'form-group' ,
12526 //cls : 'input-group-addon',
12527 html : this.fieldLabel
12535 value : this.value,
12536 cls : 'd-none form-control'
12541 multiple : 'multiple',
12543 cls : 'd-none roo-card-upload-selector'
12547 cls : 'roo-card-uploader-button-container w-100 mb-2'
12550 cls : 'card-columns roo-card-uploader-container'
12560 getChildContainer : function() /// what children are added to.
12562 return this.containerEl;
12565 getButtonContainer : function() /// what children are added to.
12567 return this.el.select(".roo-card-uploader-button-container").first();
12570 initEvents : function()
12573 Roo.bootstrap.Input.prototype.initEvents.call(this);
12577 xns: Roo.bootstrap,
12580 container_method : 'getButtonContainer' ,
12581 html : this.html, // fix changable?
12584 'click' : function(btn, e) {
12593 this.urlAPI = (window.createObjectURL && window) ||
12594 (window.URL && URL.revokeObjectURL && URL) ||
12595 (window.webkitURL && webkitURL);
12600 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12602 this.selectorEl.on('change', this.onFileSelected, this);
12605 this.images.forEach(function(img) {
12608 this.images = false;
12610 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12616 onClick : function(e)
12618 e.preventDefault();
12620 this.selectorEl.dom.click();
12624 onFileSelected : function(e)
12626 e.preventDefault();
12628 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12632 Roo.each(this.selectorEl.dom.files, function(file){
12633 this.addFile(file);
12642 addFile : function(file)
12645 if(typeof(file) === 'string'){
12646 throw "Add file by name?"; // should not happen
12650 if(!file || !this.urlAPI){
12660 var url = _this.urlAPI.createObjectURL( file);
12663 id : Roo.bootstrap.CardUploader.ID--,
12664 is_uploaded : false,
12668 mimetype : file.type,
12675 addCard : function (data)
12677 // hidden input element?
12678 // if the file is not an image...
12679 //then we need to use something other that and header_image
12684 xns : Roo.bootstrap,
12685 xtype : 'CardFooter',
12688 xns : Roo.bootstrap,
12694 xns : Roo.bootstrap,
12696 html : String.format("<small>{0}</small>", data.title),
12697 cls : 'col-11 text-left',
12702 click : function() {
12703 this.downloadCard(data.id)
12709 xns : Roo.bootstrap,
12717 click : function() {
12718 t.removeCard(data.id)
12730 var cn = this.addxtype(
12733 xns : Roo.bootstrap,
12736 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12737 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12738 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12743 initEvents : function() {
12744 Roo.bootstrap.Card.prototype.initEvents.call(this);
12745 this.imgEl = this.el.select('.card-img-top').first();
12747 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12748 this.imgEl.set({ 'pointer' : 'cursor' });
12757 // dont' really need ot update items.
12758 // this.items.push(cn);
12759 this.fileCollection.add(cn);
12761 if (!data.srcfile) {
12762 this.updateInput();
12767 var reader = new FileReader();
12768 reader.addEventListener("load", function() {
12769 data.srcdata = reader.result;
12772 reader.readAsDataURL(data.srcfile);
12777 removeCard : function(id)
12780 var card = this.fileCollection.get(id);
12781 card.data.is_deleted = 1;
12782 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12783 //this.fileCollection.remove(card);
12784 //this.items = this.items.filter(function(e) { return e != card });
12785 // dont' really need ot update items.
12786 card.el.dom.parentNode.removeChild(card.el.dom);
12787 this.updateInput();
12793 this.fileCollection.each(function(card) {
12794 if (card.el.dom && card.el.dom.parentNode) {
12795 card.el.dom.parentNode.removeChild(card.el.dom);
12798 this.fileCollection.clear();
12799 this.updateInput();
12802 updateInput : function()
12805 this.fileCollection.each(function(e) {
12809 this.inputEl().dom.value = JSON.stringify(data);
12819 Roo.bootstrap.CardUploader.ID = -1;/*
12821 * Ext JS Library 1.1.1
12822 * Copyright(c) 2006-2007, Ext JS, LLC.
12824 * Originally Released Under LGPL - original licence link has changed is not relivant.
12827 * <script type="text/javascript">
12832 * @class Roo.data.SortTypes
12834 * Defines the default sorting (casting?) comparison functions used when sorting data.
12836 Roo.data.SortTypes = {
12838 * Default sort that does nothing
12839 * @param {Mixed} s The value being converted
12840 * @return {Mixed} The comparison value
12842 none : function(s){
12847 * The regular expression used to strip tags
12851 stripTagsRE : /<\/?[^>]+>/gi,
12854 * Strips all HTML tags to sort on text only
12855 * @param {Mixed} s The value being converted
12856 * @return {String} The comparison value
12858 asText : function(s){
12859 return String(s).replace(this.stripTagsRE, "");
12863 * Strips all HTML tags to sort on text only - Case insensitive
12864 * @param {Mixed} s The value being converted
12865 * @return {String} The comparison value
12867 asUCText : function(s){
12868 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12872 * Case insensitive string
12873 * @param {Mixed} s The value being converted
12874 * @return {String} The comparison value
12876 asUCString : function(s) {
12877 return String(s).toUpperCase();
12882 * @param {Mixed} s The value being converted
12883 * @return {Number} The comparison value
12885 asDate : function(s) {
12889 if(s instanceof Date){
12890 return s.getTime();
12892 return Date.parse(String(s));
12897 * @param {Mixed} s The value being converted
12898 * @return {Float} The comparison value
12900 asFloat : function(s) {
12901 var val = parseFloat(String(s).replace(/,/g, ""));
12910 * @param {Mixed} s The value being converted
12911 * @return {Number} The comparison value
12913 asInt : function(s) {
12914 var val = parseInt(String(s).replace(/,/g, ""));
12922 * Ext JS Library 1.1.1
12923 * Copyright(c) 2006-2007, Ext JS, LLC.
12925 * Originally Released Under LGPL - original licence link has changed is not relivant.
12928 * <script type="text/javascript">
12932 * @class Roo.data.Record
12933 * Instances of this class encapsulate both record <em>definition</em> information, and record
12934 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12935 * to access Records cached in an {@link Roo.data.Store} object.<br>
12937 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12938 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12941 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12943 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12944 * {@link #create}. The parameters are the same.
12945 * @param {Array} data An associative Array of data values keyed by the field name.
12946 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12947 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12948 * not specified an integer id is generated.
12950 Roo.data.Record = function(data, id){
12951 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12956 * Generate a constructor for a specific record layout.
12957 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12958 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12959 * Each field definition object may contain the following properties: <ul>
12960 * <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,
12961 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12962 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12963 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12964 * is being used, then this is a string containing the javascript expression to reference the data relative to
12965 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12966 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12967 * this may be omitted.</p></li>
12968 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12969 * <ul><li>auto (Default, implies no conversion)</li>
12974 * <li>date</li></ul></p></li>
12975 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12976 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12977 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12978 * by the Reader into an object that will be stored in the Record. It is passed the
12979 * following parameters:<ul>
12980 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12982 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12984 * <br>usage:<br><pre><code>
12985 var TopicRecord = Roo.data.Record.create(
12986 {name: 'title', mapping: 'topic_title'},
12987 {name: 'author', mapping: 'username'},
12988 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12989 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12990 {name: 'lastPoster', mapping: 'user2'},
12991 {name: 'excerpt', mapping: 'post_text'}
12994 var myNewRecord = new TopicRecord({
12995 title: 'Do my job please',
12998 lastPost: new Date(),
12999 lastPoster: 'Animal',
13000 excerpt: 'No way dude!'
13002 myStore.add(myNewRecord);
13007 Roo.data.Record.create = function(o){
13008 var f = function(){
13009 f.superclass.constructor.apply(this, arguments);
13011 Roo.extend(f, Roo.data.Record);
13012 var p = f.prototype;
13013 p.fields = new Roo.util.MixedCollection(false, function(field){
13016 for(var i = 0, len = o.length; i < len; i++){
13017 p.fields.add(new Roo.data.Field(o[i]));
13019 f.getField = function(name){
13020 return p.fields.get(name);
13025 Roo.data.Record.AUTO_ID = 1000;
13026 Roo.data.Record.EDIT = 'edit';
13027 Roo.data.Record.REJECT = 'reject';
13028 Roo.data.Record.COMMIT = 'commit';
13030 Roo.data.Record.prototype = {
13032 * Readonly flag - true if this record has been modified.
13041 join : function(store){
13042 this.store = store;
13046 * Set the named field to the specified value.
13047 * @param {String} name The name of the field to set.
13048 * @param {Object} value The value to set the field to.
13050 set : function(name, value){
13051 if(this.data[name] == value){
13055 if(!this.modified){
13056 this.modified = {};
13058 if(typeof this.modified[name] == 'undefined'){
13059 this.modified[name] = this.data[name];
13061 this.data[name] = value;
13062 if(!this.editing && this.store){
13063 this.store.afterEdit(this);
13068 * Get the value of the named field.
13069 * @param {String} name The name of the field to get the value of.
13070 * @return {Object} The value of the field.
13072 get : function(name){
13073 return this.data[name];
13077 beginEdit : function(){
13078 this.editing = true;
13079 this.modified = {};
13083 cancelEdit : function(){
13084 this.editing = false;
13085 delete this.modified;
13089 endEdit : function(){
13090 this.editing = false;
13091 if(this.dirty && this.store){
13092 this.store.afterEdit(this);
13097 * Usually called by the {@link Roo.data.Store} which owns the Record.
13098 * Rejects all changes made to the Record since either creation, or the last commit operation.
13099 * Modified fields are reverted to their original values.
13101 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13102 * of reject operations.
13104 reject : function(){
13105 var m = this.modified;
13107 if(typeof m[n] != "function"){
13108 this.data[n] = m[n];
13111 this.dirty = false;
13112 delete this.modified;
13113 this.editing = false;
13115 this.store.afterReject(this);
13120 * Usually called by the {@link Roo.data.Store} which owns the Record.
13121 * Commits all changes made to the Record since either creation, or the last commit operation.
13123 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13124 * of commit operations.
13126 commit : function(){
13127 this.dirty = false;
13128 delete this.modified;
13129 this.editing = false;
13131 this.store.afterCommit(this);
13136 hasError : function(){
13137 return this.error != null;
13141 clearError : function(){
13146 * Creates a copy of this record.
13147 * @param {String} id (optional) A new record id if you don't want to use this record's id
13150 copy : function(newId) {
13151 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13155 * Ext JS Library 1.1.1
13156 * Copyright(c) 2006-2007, Ext JS, LLC.
13158 * Originally Released Under LGPL - original licence link has changed is not relivant.
13161 * <script type="text/javascript">
13167 * @class Roo.data.Store
13168 * @extends Roo.util.Observable
13169 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13170 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13172 * 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
13173 * has no knowledge of the format of the data returned by the Proxy.<br>
13175 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13176 * instances from the data object. These records are cached and made available through accessor functions.
13178 * Creates a new Store.
13179 * @param {Object} config A config object containing the objects needed for the Store to access data,
13180 * and read the data into Records.
13182 Roo.data.Store = function(config){
13183 this.data = new Roo.util.MixedCollection(false);
13184 this.data.getKey = function(o){
13187 this.baseParams = {};
13189 this.paramNames = {
13194 "multisort" : "_multisort"
13197 if(config && config.data){
13198 this.inlineData = config.data;
13199 delete config.data;
13202 Roo.apply(this, config);
13204 if(this.reader){ // reader passed
13205 this.reader = Roo.factory(this.reader, Roo.data);
13206 this.reader.xmodule = this.xmodule || false;
13207 if(!this.recordType){
13208 this.recordType = this.reader.recordType;
13210 if(this.reader.onMetaChange){
13211 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13215 if(this.recordType){
13216 this.fields = this.recordType.prototype.fields;
13218 this.modified = [];
13222 * @event datachanged
13223 * Fires when the data cache has changed, and a widget which is using this Store
13224 * as a Record cache should refresh its view.
13225 * @param {Store} this
13227 datachanged : true,
13229 * @event metachange
13230 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13231 * @param {Store} this
13232 * @param {Object} meta The JSON metadata
13237 * Fires when Records have been added to the Store
13238 * @param {Store} this
13239 * @param {Roo.data.Record[]} records The array of Records added
13240 * @param {Number} index The index at which the record(s) were added
13245 * Fires when a Record has been removed from the Store
13246 * @param {Store} this
13247 * @param {Roo.data.Record} record The Record that was removed
13248 * @param {Number} index The index at which the record was removed
13253 * Fires when a Record has been updated
13254 * @param {Store} this
13255 * @param {Roo.data.Record} record The Record that was updated
13256 * @param {String} operation The update operation being performed. Value may be one of:
13258 Roo.data.Record.EDIT
13259 Roo.data.Record.REJECT
13260 Roo.data.Record.COMMIT
13266 * Fires when the data cache has been cleared.
13267 * @param {Store} this
13271 * @event beforeload
13272 * Fires before a request is made for a new data object. If the beforeload handler returns false
13273 * the load action will be canceled.
13274 * @param {Store} this
13275 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13279 * @event beforeloadadd
13280 * Fires after a new set of Records has been loaded.
13281 * @param {Store} this
13282 * @param {Roo.data.Record[]} records The Records that were loaded
13283 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13285 beforeloadadd : true,
13288 * Fires after a new set of Records has been loaded, before they are added to the store.
13289 * @param {Store} this
13290 * @param {Roo.data.Record[]} records The Records that were loaded
13291 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13292 * @params {Object} return from reader
13296 * @event loadexception
13297 * Fires if an exception occurs in the Proxy during loading.
13298 * Called with the signature of the Proxy's "loadexception" event.
13299 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13302 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13303 * @param {Object} load options
13304 * @param {Object} jsonData from your request (normally this contains the Exception)
13306 loadexception : true
13310 this.proxy = Roo.factory(this.proxy, Roo.data);
13311 this.proxy.xmodule = this.xmodule || false;
13312 this.relayEvents(this.proxy, ["loadexception"]);
13314 this.sortToggle = {};
13315 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13317 Roo.data.Store.superclass.constructor.call(this);
13319 if(this.inlineData){
13320 this.loadData(this.inlineData);
13321 delete this.inlineData;
13325 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13327 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13328 * without a remote query - used by combo/forms at present.
13332 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13335 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13338 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13339 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13342 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13343 * on any HTTP request
13346 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13349 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13353 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13354 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13356 remoteSort : false,
13359 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13360 * loaded or when a record is removed. (defaults to false).
13362 pruneModifiedRecords : false,
13365 lastOptions : null,
13368 * Add Records to the Store and fires the add event.
13369 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13371 add : function(records){
13372 records = [].concat(records);
13373 for(var i = 0, len = records.length; i < len; i++){
13374 records[i].join(this);
13376 var index = this.data.length;
13377 this.data.addAll(records);
13378 this.fireEvent("add", this, records, index);
13382 * Remove a Record from the Store and fires the remove event.
13383 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13385 remove : function(record){
13386 var index = this.data.indexOf(record);
13387 this.data.removeAt(index);
13389 if(this.pruneModifiedRecords){
13390 this.modified.remove(record);
13392 this.fireEvent("remove", this, record, index);
13396 * Remove all Records from the Store and fires the clear event.
13398 removeAll : function(){
13400 if(this.pruneModifiedRecords){
13401 this.modified = [];
13403 this.fireEvent("clear", this);
13407 * Inserts Records to the Store at the given index and fires the add event.
13408 * @param {Number} index The start index at which to insert the passed Records.
13409 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13411 insert : function(index, records){
13412 records = [].concat(records);
13413 for(var i = 0, len = records.length; i < len; i++){
13414 this.data.insert(index, records[i]);
13415 records[i].join(this);
13417 this.fireEvent("add", this, records, index);
13421 * Get the index within the cache of the passed Record.
13422 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13423 * @return {Number} The index of the passed Record. Returns -1 if not found.
13425 indexOf : function(record){
13426 return this.data.indexOf(record);
13430 * Get the index within the cache of the Record with the passed id.
13431 * @param {String} id The id of the Record to find.
13432 * @return {Number} The index of the Record. Returns -1 if not found.
13434 indexOfId : function(id){
13435 return this.data.indexOfKey(id);
13439 * Get the Record with the specified id.
13440 * @param {String} id The id of the Record to find.
13441 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13443 getById : function(id){
13444 return this.data.key(id);
13448 * Get the Record at the specified index.
13449 * @param {Number} index The index of the Record to find.
13450 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13452 getAt : function(index){
13453 return this.data.itemAt(index);
13457 * Returns a range of Records between specified indices.
13458 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13459 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13460 * @return {Roo.data.Record[]} An array of Records
13462 getRange : function(start, end){
13463 return this.data.getRange(start, end);
13467 storeOptions : function(o){
13468 o = Roo.apply({}, o);
13471 this.lastOptions = o;
13475 * Loads the Record cache from the configured Proxy using the configured Reader.
13477 * If using remote paging, then the first load call must specify the <em>start</em>
13478 * and <em>limit</em> properties in the options.params property to establish the initial
13479 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13481 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13482 * and this call will return before the new data has been loaded. Perform any post-processing
13483 * in a callback function, or in a "load" event handler.</strong>
13485 * @param {Object} options An object containing properties which control loading options:<ul>
13486 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13487 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13488 * passed the following arguments:<ul>
13489 * <li>r : Roo.data.Record[]</li>
13490 * <li>options: Options object from the load call</li>
13491 * <li>success: Boolean success indicator</li></ul></li>
13492 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13493 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13496 load : function(options){
13497 options = options || {};
13498 if(this.fireEvent("beforeload", this, options) !== false){
13499 this.storeOptions(options);
13500 var p = Roo.apply(options.params || {}, this.baseParams);
13501 // if meta was not loaded from remote source.. try requesting it.
13502 if (!this.reader.metaFromRemote) {
13503 p._requestMeta = 1;
13505 if(this.sortInfo && this.remoteSort){
13506 var pn = this.paramNames;
13507 p[pn["sort"]] = this.sortInfo.field;
13508 p[pn["dir"]] = this.sortInfo.direction;
13510 if (this.multiSort) {
13511 var pn = this.paramNames;
13512 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13515 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13520 * Reloads the Record cache from the configured Proxy using the configured Reader and
13521 * the options from the last load operation performed.
13522 * @param {Object} options (optional) An object containing properties which may override the options
13523 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13524 * the most recently used options are reused).
13526 reload : function(options){
13527 this.load(Roo.applyIf(options||{}, this.lastOptions));
13531 // Called as a callback by the Reader during a load operation.
13532 loadRecords : function(o, options, success){
13533 if(!o || success === false){
13534 if(success !== false){
13535 this.fireEvent("load", this, [], options, o);
13537 if(options.callback){
13538 options.callback.call(options.scope || this, [], options, false);
13542 // if data returned failure - throw an exception.
13543 if (o.success === false) {
13544 // show a message if no listener is registered.
13545 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13546 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13548 // loadmask wil be hooked into this..
13549 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13552 var r = o.records, t = o.totalRecords || r.length;
13554 this.fireEvent("beforeloadadd", this, r, options, o);
13556 if(!options || options.add !== true){
13557 if(this.pruneModifiedRecords){
13558 this.modified = [];
13560 for(var i = 0, len = r.length; i < len; i++){
13564 this.data = this.snapshot;
13565 delete this.snapshot;
13568 this.data.addAll(r);
13569 this.totalLength = t;
13571 this.fireEvent("datachanged", this);
13573 this.totalLength = Math.max(t, this.data.length+r.length);
13577 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13579 var e = new Roo.data.Record({});
13581 e.set(this.parent.displayField, this.parent.emptyTitle);
13582 e.set(this.parent.valueField, '');
13587 this.fireEvent("load", this, r, options, o);
13588 if(options.callback){
13589 options.callback.call(options.scope || this, r, options, true);
13595 * Loads data from a passed data block. A Reader which understands the format of the data
13596 * must have been configured in the constructor.
13597 * @param {Object} data The data block from which to read the Records. The format of the data expected
13598 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13599 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13601 loadData : function(o, append){
13602 var r = this.reader.readRecords(o);
13603 this.loadRecords(r, {add: append}, true);
13607 * using 'cn' the nested child reader read the child array into it's child stores.
13608 * @param {Object} rec The record with a 'children array
13610 loadDataFromChildren : function(rec)
13612 this.loadData(this.reader.toLoadData(rec));
13617 * Gets the number of cached records.
13619 * <em>If using paging, this may not be the total size of the dataset. If the data object
13620 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13621 * the data set size</em>
13623 getCount : function(){
13624 return this.data.length || 0;
13628 * Gets the total number of records in the dataset as returned by the server.
13630 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13631 * the dataset size</em>
13633 getTotalCount : function(){
13634 return this.totalLength || 0;
13638 * Returns the sort state of the Store as an object with two properties:
13640 field {String} The name of the field by which the Records are sorted
13641 direction {String} The sort order, "ASC" or "DESC"
13644 getSortState : function(){
13645 return this.sortInfo;
13649 applySort : function(){
13650 if(this.sortInfo && !this.remoteSort){
13651 var s = this.sortInfo, f = s.field;
13652 var st = this.fields.get(f).sortType;
13653 var fn = function(r1, r2){
13654 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13655 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13657 this.data.sort(s.direction, fn);
13658 if(this.snapshot && this.snapshot != this.data){
13659 this.snapshot.sort(s.direction, fn);
13665 * Sets the default sort column and order to be used by the next load operation.
13666 * @param {String} fieldName The name of the field to sort by.
13667 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13669 setDefaultSort : function(field, dir){
13670 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13674 * Sort the Records.
13675 * If remote sorting is used, the sort is performed on the server, and the cache is
13676 * reloaded. If local sorting is used, the cache is sorted internally.
13677 * @param {String} fieldName The name of the field to sort by.
13678 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13680 sort : function(fieldName, dir){
13681 var f = this.fields.get(fieldName);
13683 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13685 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13686 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13691 this.sortToggle[f.name] = dir;
13692 this.sortInfo = {field: f.name, direction: dir};
13693 if(!this.remoteSort){
13695 this.fireEvent("datachanged", this);
13697 this.load(this.lastOptions);
13702 * Calls the specified function for each of the Records in the cache.
13703 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13704 * Returning <em>false</em> aborts and exits the iteration.
13705 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13707 each : function(fn, scope){
13708 this.data.each(fn, scope);
13712 * Gets all records modified since the last commit. Modified records are persisted across load operations
13713 * (e.g., during paging).
13714 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13716 getModifiedRecords : function(){
13717 return this.modified;
13721 createFilterFn : function(property, value, anyMatch){
13722 if(!value.exec){ // not a regex
13723 value = String(value);
13724 if(value.length == 0){
13727 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13729 return function(r){
13730 return value.test(r.data[property]);
13735 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13736 * @param {String} property A field on your records
13737 * @param {Number} start The record index to start at (defaults to 0)
13738 * @param {Number} end The last record index to include (defaults to length - 1)
13739 * @return {Number} The sum
13741 sum : function(property, start, end){
13742 var rs = this.data.items, v = 0;
13743 start = start || 0;
13744 end = (end || end === 0) ? end : rs.length-1;
13746 for(var i = start; i <= end; i++){
13747 v += (rs[i].data[property] || 0);
13753 * Filter the records by a specified property.
13754 * @param {String} field A field on your records
13755 * @param {String/RegExp} value Either a string that the field
13756 * should start with or a RegExp to test against the field
13757 * @param {Boolean} anyMatch True to match any part not just the beginning
13759 filter : function(property, value, anyMatch){
13760 var fn = this.createFilterFn(property, value, anyMatch);
13761 return fn ? this.filterBy(fn) : this.clearFilter();
13765 * Filter by a function. The specified function will be called with each
13766 * record in this data source. If the function returns true the record is included,
13767 * otherwise it is filtered.
13768 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769 * @param {Object} scope (optional) The scope of the function (defaults to this)
13771 filterBy : function(fn, scope){
13772 this.snapshot = this.snapshot || this.data;
13773 this.data = this.queryBy(fn, scope||this);
13774 this.fireEvent("datachanged", this);
13778 * Query the records by a specified property.
13779 * @param {String} field A field on your records
13780 * @param {String/RegExp} value Either a string that the field
13781 * should start with or a RegExp to test against the field
13782 * @param {Boolean} anyMatch True to match any part not just the beginning
13783 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13785 query : function(property, value, anyMatch){
13786 var fn = this.createFilterFn(property, value, anyMatch);
13787 return fn ? this.queryBy(fn) : this.data.clone();
13791 * Query by a function. The specified function will be called with each
13792 * record in this data source. If the function returns true the record is included
13794 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13795 * @param {Object} scope (optional) The scope of the function (defaults to this)
13796 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13798 queryBy : function(fn, scope){
13799 var data = this.snapshot || this.data;
13800 return data.filterBy(fn, scope||this);
13804 * Collects unique values for a particular dataIndex from this store.
13805 * @param {String} dataIndex The property to collect
13806 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13807 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13808 * @return {Array} An array of the unique values
13810 collect : function(dataIndex, allowNull, bypassFilter){
13811 var d = (bypassFilter === true && this.snapshot) ?
13812 this.snapshot.items : this.data.items;
13813 var v, sv, r = [], l = {};
13814 for(var i = 0, len = d.length; i < len; i++){
13815 v = d[i].data[dataIndex];
13817 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13826 * Revert to a view of the Record cache with no filtering applied.
13827 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13829 clearFilter : function(suppressEvent){
13830 if(this.snapshot && this.snapshot != this.data){
13831 this.data = this.snapshot;
13832 delete this.snapshot;
13833 if(suppressEvent !== true){
13834 this.fireEvent("datachanged", this);
13840 afterEdit : function(record){
13841 if(this.modified.indexOf(record) == -1){
13842 this.modified.push(record);
13844 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13848 afterReject : function(record){
13849 this.modified.remove(record);
13850 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13854 afterCommit : function(record){
13855 this.modified.remove(record);
13856 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13860 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13861 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13863 commitChanges : function(){
13864 var m = this.modified.slice(0);
13865 this.modified = [];
13866 for(var i = 0, len = m.length; i < len; i++){
13872 * Cancel outstanding changes on all changed records.
13874 rejectChanges : function(){
13875 var m = this.modified.slice(0);
13876 this.modified = [];
13877 for(var i = 0, len = m.length; i < len; i++){
13882 onMetaChange : function(meta, rtype, o){
13883 this.recordType = rtype;
13884 this.fields = rtype.prototype.fields;
13885 delete this.snapshot;
13886 this.sortInfo = meta.sortInfo || this.sortInfo;
13887 this.modified = [];
13888 this.fireEvent('metachange', this, this.reader.meta);
13891 moveIndex : function(data, type)
13893 var index = this.indexOf(data);
13895 var newIndex = index + type;
13899 this.insert(newIndex, data);
13904 * Ext JS Library 1.1.1
13905 * Copyright(c) 2006-2007, Ext JS, LLC.
13907 * Originally Released Under LGPL - original licence link has changed is not relivant.
13910 * <script type="text/javascript">
13914 * @class Roo.data.SimpleStore
13915 * @extends Roo.data.Store
13916 * Small helper class to make creating Stores from Array data easier.
13917 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13918 * @cfg {Array} fields An array of field definition objects, or field name strings.
13919 * @cfg {Object} an existing reader (eg. copied from another store)
13920 * @cfg {Array} data The multi-dimensional array of data
13922 * @param {Object} config
13924 Roo.data.SimpleStore = function(config)
13926 Roo.data.SimpleStore.superclass.constructor.call(this, {
13928 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13931 Roo.data.Record.create(config.fields)
13933 proxy : new Roo.data.MemoryProxy(config.data)
13937 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13939 * Ext JS Library 1.1.1
13940 * Copyright(c) 2006-2007, Ext JS, LLC.
13942 * Originally Released Under LGPL - original licence link has changed is not relivant.
13945 * <script type="text/javascript">
13950 * @extends Roo.data.Store
13951 * @class Roo.data.JsonStore
13952 * Small helper class to make creating Stores for JSON data easier. <br/>
13954 var store = new Roo.data.JsonStore({
13955 url: 'get-images.php',
13957 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13960 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13961 * JsonReader and HttpProxy (unless inline data is provided).</b>
13962 * @cfg {Array} fields An array of field definition objects, or field name strings.
13964 * @param {Object} config
13966 Roo.data.JsonStore = function(c){
13967 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13968 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13969 reader: new Roo.data.JsonReader(c, c.fields)
13972 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13974 * Ext JS Library 1.1.1
13975 * Copyright(c) 2006-2007, Ext JS, LLC.
13977 * Originally Released Under LGPL - original licence link has changed is not relivant.
13980 * <script type="text/javascript">
13984 Roo.data.Field = function(config){
13985 if(typeof config == "string"){
13986 config = {name: config};
13988 Roo.apply(this, config);
13991 this.type = "auto";
13994 var st = Roo.data.SortTypes;
13995 // named sortTypes are supported, here we look them up
13996 if(typeof this.sortType == "string"){
13997 this.sortType = st[this.sortType];
14000 // set default sortType for strings and dates
14001 if(!this.sortType){
14004 this.sortType = st.asUCString;
14007 this.sortType = st.asDate;
14010 this.sortType = st.none;
14015 var stripRe = /[\$,%]/g;
14017 // prebuilt conversion function for this field, instead of
14018 // switching every time we're reading a value
14020 var cv, dateFormat = this.dateFormat;
14025 cv = function(v){ return v; };
14028 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14032 return v !== undefined && v !== null && v !== '' ?
14033 parseInt(String(v).replace(stripRe, ""), 10) : '';
14038 return v !== undefined && v !== null && v !== '' ?
14039 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14044 cv = function(v){ return v === true || v === "true" || v == 1; };
14051 if(v instanceof Date){
14055 if(dateFormat == "timestamp"){
14056 return new Date(v*1000);
14058 return Date.parseDate(v, dateFormat);
14060 var parsed = Date.parse(v);
14061 return parsed ? new Date(parsed) : null;
14070 Roo.data.Field.prototype = {
14078 * Ext JS Library 1.1.1
14079 * Copyright(c) 2006-2007, Ext JS, LLC.
14081 * Originally Released Under LGPL - original licence link has changed is not relivant.
14084 * <script type="text/javascript">
14087 // Base class for reading structured data from a data source. This class is intended to be
14088 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14091 * @class Roo.data.DataReader
14092 * Base class for reading structured data from a data source. This class is intended to be
14093 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14096 Roo.data.DataReader = function(meta, recordType){
14100 this.recordType = recordType instanceof Array ?
14101 Roo.data.Record.create(recordType) : recordType;
14104 Roo.data.DataReader.prototype = {
14107 readerType : 'Data',
14109 * Create an empty record
14110 * @param {Object} data (optional) - overlay some values
14111 * @return {Roo.data.Record} record created.
14113 newRow : function(d) {
14115 this.recordType.prototype.fields.each(function(c) {
14117 case 'int' : da[c.name] = 0; break;
14118 case 'date' : da[c.name] = new Date(); break;
14119 case 'float' : da[c.name] = 0.0; break;
14120 case 'boolean' : da[c.name] = false; break;
14121 default : da[c.name] = ""; break;
14125 return new this.recordType(Roo.apply(da, d));
14131 * Ext JS Library 1.1.1
14132 * Copyright(c) 2006-2007, Ext JS, LLC.
14134 * Originally Released Under LGPL - original licence link has changed is not relivant.
14137 * <script type="text/javascript">
14141 * @class Roo.data.DataProxy
14142 * @extends Roo.data.Observable
14143 * This class is an abstract base class for implementations which provide retrieval of
14144 * unformatted data objects.<br>
14146 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14147 * (of the appropriate type which knows how to parse the data object) to provide a block of
14148 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14150 * Custom implementations must implement the load method as described in
14151 * {@link Roo.data.HttpProxy#load}.
14153 Roo.data.DataProxy = function(){
14156 * @event beforeload
14157 * Fires before a network request is made to retrieve a data object.
14158 * @param {Object} This DataProxy object.
14159 * @param {Object} params The params parameter to the load function.
14164 * Fires before the load method's callback is called.
14165 * @param {Object} This DataProxy object.
14166 * @param {Object} o The data object.
14167 * @param {Object} arg The callback argument object passed to the load function.
14171 * @event loadexception
14172 * Fires if an Exception occurs during data retrieval.
14173 * @param {Object} This DataProxy object.
14174 * @param {Object} o The data object.
14175 * @param {Object} arg The callback argument object passed to the load function.
14176 * @param {Object} e The Exception.
14178 loadexception : true
14180 Roo.data.DataProxy.superclass.constructor.call(this);
14183 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14186 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14190 * Ext JS Library 1.1.1
14191 * Copyright(c) 2006-2007, Ext JS, LLC.
14193 * Originally Released Under LGPL - original licence link has changed is not relivant.
14196 * <script type="text/javascript">
14199 * @class Roo.data.MemoryProxy
14200 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14201 * to the Reader when its load method is called.
14203 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14205 Roo.data.MemoryProxy = function(data){
14209 Roo.data.MemoryProxy.superclass.constructor.call(this);
14213 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14216 * Load data from the requested source (in this case an in-memory
14217 * data object passed to the constructor), read the data object into
14218 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14219 * process that block using the passed callback.
14220 * @param {Object} params This parameter is not used by the MemoryProxy class.
14221 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14222 * object into a block of Roo.data.Records.
14223 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14224 * The function must be passed <ul>
14225 * <li>The Record block object</li>
14226 * <li>The "arg" argument from the load function</li>
14227 * <li>A boolean success indicator</li>
14229 * @param {Object} scope The scope in which to call the callback
14230 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14232 load : function(params, reader, callback, scope, arg){
14233 params = params || {};
14236 result = reader.readRecords(params.data ? params.data :this.data);
14238 this.fireEvent("loadexception", this, arg, null, e);
14239 callback.call(scope, null, arg, false);
14242 callback.call(scope, result, arg, true);
14246 update : function(params, records){
14251 * Ext JS Library 1.1.1
14252 * Copyright(c) 2006-2007, Ext JS, LLC.
14254 * Originally Released Under LGPL - original licence link has changed is not relivant.
14257 * <script type="text/javascript">
14260 * @class Roo.data.HttpProxy
14261 * @extends Roo.data.DataProxy
14262 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14263 * configured to reference a certain URL.<br><br>
14265 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14266 * from which the running page was served.<br><br>
14268 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14270 * Be aware that to enable the browser to parse an XML document, the server must set
14271 * the Content-Type header in the HTTP response to "text/xml".
14273 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14274 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14275 * will be used to make the request.
14277 Roo.data.HttpProxy = function(conn){
14278 Roo.data.HttpProxy.superclass.constructor.call(this);
14279 // is conn a conn config or a real conn?
14281 this.useAjax = !conn || !conn.events;
14285 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14286 // thse are take from connection...
14289 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14292 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14293 * extra parameters to each request made by this object. (defaults to undefined)
14296 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14297 * to each request made by this object. (defaults to undefined)
14300 * @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)
14303 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14306 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14312 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14316 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14317 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14318 * a finer-grained basis than the DataProxy events.
14320 getConnection : function(){
14321 return this.useAjax ? Roo.Ajax : this.conn;
14325 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14326 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14327 * process that block using the passed callback.
14328 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14329 * for the request to the remote server.
14330 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14331 * object into a block of Roo.data.Records.
14332 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14333 * The function must be passed <ul>
14334 * <li>The Record block object</li>
14335 * <li>The "arg" argument from the load function</li>
14336 * <li>A boolean success indicator</li>
14338 * @param {Object} scope The scope in which to call the callback
14339 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14341 load : function(params, reader, callback, scope, arg){
14342 if(this.fireEvent("beforeload", this, params) !== false){
14344 params : params || {},
14346 callback : callback,
14351 callback : this.loadResponse,
14355 Roo.applyIf(o, this.conn);
14356 if(this.activeRequest){
14357 Roo.Ajax.abort(this.activeRequest);
14359 this.activeRequest = Roo.Ajax.request(o);
14361 this.conn.request(o);
14364 callback.call(scope||this, null, arg, false);
14369 loadResponse : function(o, success, response){
14370 delete this.activeRequest;
14372 this.fireEvent("loadexception", this, o, response);
14373 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14378 result = o.reader.read(response);
14380 this.fireEvent("loadexception", this, o, response, e);
14381 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14385 this.fireEvent("load", this, o, o.request.arg);
14386 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14390 update : function(dataSet){
14395 updateResponse : function(dataSet){
14400 * Ext JS Library 1.1.1
14401 * Copyright(c) 2006-2007, Ext JS, LLC.
14403 * Originally Released Under LGPL - original licence link has changed is not relivant.
14406 * <script type="text/javascript">
14410 * @class Roo.data.ScriptTagProxy
14411 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14412 * other than the originating domain of the running page.<br><br>
14414 * <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
14415 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14417 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14418 * source code that is used as the source inside a <script> tag.<br><br>
14420 * In order for the browser to process the returned data, the server must wrap the data object
14421 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14422 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14423 * depending on whether the callback name was passed:
14426 boolean scriptTag = false;
14427 String cb = request.getParameter("callback");
14430 response.setContentType("text/javascript");
14432 response.setContentType("application/x-json");
14434 Writer out = response.getWriter();
14436 out.write(cb + "(");
14438 out.print(dataBlock.toJsonString());
14445 * @param {Object} config A configuration object.
14447 Roo.data.ScriptTagProxy = function(config){
14448 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14449 Roo.apply(this, config);
14450 this.head = document.getElementsByTagName("head")[0];
14453 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14455 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14457 * @cfg {String} url The URL from which to request the data object.
14460 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14464 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14465 * the server the name of the callback function set up by the load call to process the returned data object.
14466 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14467 * javascript output which calls this named function passing the data object as its only parameter.
14469 callbackParam : "callback",
14471 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14472 * name to the request.
14477 * Load data from the configured URL, read the data object into
14478 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14479 * process that block using the passed callback.
14480 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14481 * for the request to the remote server.
14482 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14483 * object into a block of Roo.data.Records.
14484 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14485 * The function must be passed <ul>
14486 * <li>The Record block object</li>
14487 * <li>The "arg" argument from the load function</li>
14488 * <li>A boolean success indicator</li>
14490 * @param {Object} scope The scope in which to call the callback
14491 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14493 load : function(params, reader, callback, scope, arg){
14494 if(this.fireEvent("beforeload", this, params) !== false){
14496 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14498 var url = this.url;
14499 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14501 url += "&_dc=" + (new Date().getTime());
14503 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14506 cb : "stcCallback"+transId,
14507 scriptId : "stcScript"+transId,
14511 callback : callback,
14517 window[trans.cb] = function(o){
14518 conn.handleResponse(o, trans);
14521 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14523 if(this.autoAbort !== false){
14527 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14529 var script = document.createElement("script");
14530 script.setAttribute("src", url);
14531 script.setAttribute("type", "text/javascript");
14532 script.setAttribute("id", trans.scriptId);
14533 this.head.appendChild(script);
14535 this.trans = trans;
14537 callback.call(scope||this, null, arg, false);
14542 isLoading : function(){
14543 return this.trans ? true : false;
14547 * Abort the current server request.
14549 abort : function(){
14550 if(this.isLoading()){
14551 this.destroyTrans(this.trans);
14556 destroyTrans : function(trans, isLoaded){
14557 this.head.removeChild(document.getElementById(trans.scriptId));
14558 clearTimeout(trans.timeoutId);
14560 window[trans.cb] = undefined;
14562 delete window[trans.cb];
14565 // if hasn't been loaded, wait for load to remove it to prevent script error
14566 window[trans.cb] = function(){
14567 window[trans.cb] = undefined;
14569 delete window[trans.cb];
14576 handleResponse : function(o, trans){
14577 this.trans = false;
14578 this.destroyTrans(trans, true);
14581 result = trans.reader.readRecords(o);
14583 this.fireEvent("loadexception", this, o, trans.arg, e);
14584 trans.callback.call(trans.scope||window, null, trans.arg, false);
14587 this.fireEvent("load", this, o, trans.arg);
14588 trans.callback.call(trans.scope||window, result, trans.arg, true);
14592 handleFailure : function(trans){
14593 this.trans = false;
14594 this.destroyTrans(trans, false);
14595 this.fireEvent("loadexception", this, null, trans.arg);
14596 trans.callback.call(trans.scope||window, null, trans.arg, false);
14600 * Ext JS Library 1.1.1
14601 * Copyright(c) 2006-2007, Ext JS, LLC.
14603 * Originally Released Under LGPL - original licence link has changed is not relivant.
14606 * <script type="text/javascript">
14610 * @class Roo.data.JsonReader
14611 * @extends Roo.data.DataReader
14612 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14613 * based on mappings in a provided Roo.data.Record constructor.
14615 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14616 * in the reply previously.
14621 var RecordDef = Roo.data.Record.create([
14622 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14623 {name: 'occupation'} // This field will use "occupation" as the mapping.
14625 var myReader = new Roo.data.JsonReader({
14626 totalProperty: "results", // The property which contains the total dataset size (optional)
14627 root: "rows", // The property which contains an Array of row objects
14628 id: "id" // The property within each row object that provides an ID for the record (optional)
14632 * This would consume a JSON file like this:
14634 { 'results': 2, 'rows': [
14635 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14636 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14639 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14640 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14641 * paged from the remote server.
14642 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14643 * @cfg {String} root name of the property which contains the Array of row objects.
14644 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14645 * @cfg {Array} fields Array of field definition objects
14647 * Create a new JsonReader
14648 * @param {Object} meta Metadata configuration options
14649 * @param {Object} recordType Either an Array of field definition objects,
14650 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14652 Roo.data.JsonReader = function(meta, recordType){
14655 // set some defaults:
14656 Roo.applyIf(meta, {
14657 totalProperty: 'total',
14658 successProperty : 'success',
14663 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14665 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14667 readerType : 'Json',
14670 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14671 * Used by Store query builder to append _requestMeta to params.
14674 metaFromRemote : false,
14676 * This method is only used by a DataProxy which has retrieved data from a remote server.
14677 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14678 * @return {Object} data A data block which is used by an Roo.data.Store object as
14679 * a cache of Roo.data.Records.
14681 read : function(response){
14682 var json = response.responseText;
14684 var o = /* eval:var:o */ eval("("+json+")");
14686 throw {message: "JsonReader.read: Json object not found"};
14692 this.metaFromRemote = true;
14693 this.meta = o.metaData;
14694 this.recordType = Roo.data.Record.create(o.metaData.fields);
14695 this.onMetaChange(this.meta, this.recordType, o);
14697 return this.readRecords(o);
14700 // private function a store will implement
14701 onMetaChange : function(meta, recordType, o){
14708 simpleAccess: function(obj, subsc) {
14715 getJsonAccessor: function(){
14717 return function(expr) {
14719 return(re.test(expr))
14720 ? new Function("obj", "return obj." + expr)
14725 return Roo.emptyFn;
14730 * Create a data block containing Roo.data.Records from an XML document.
14731 * @param {Object} o An object which contains an Array of row objects in the property specified
14732 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14733 * which contains the total size of the dataset.
14734 * @return {Object} data A data block which is used by an Roo.data.Store object as
14735 * a cache of Roo.data.Records.
14737 readRecords : function(o){
14739 * After any data loads, the raw JSON data is available for further custom processing.
14743 var s = this.meta, Record = this.recordType,
14744 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14746 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14748 if(s.totalProperty) {
14749 this.getTotal = this.getJsonAccessor(s.totalProperty);
14751 if(s.successProperty) {
14752 this.getSuccess = this.getJsonAccessor(s.successProperty);
14754 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14756 var g = this.getJsonAccessor(s.id);
14757 this.getId = function(rec) {
14759 return (r === undefined || r === "") ? null : r;
14762 this.getId = function(){return null;};
14765 for(var jj = 0; jj < fl; jj++){
14767 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14768 this.ef[jj] = this.getJsonAccessor(map);
14772 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14773 if(s.totalProperty){
14774 var vt = parseInt(this.getTotal(o), 10);
14779 if(s.successProperty){
14780 var vs = this.getSuccess(o);
14781 if(vs === false || vs === 'false'){
14786 for(var i = 0; i < c; i++){
14789 var id = this.getId(n);
14790 for(var j = 0; j < fl; j++){
14792 var v = this.ef[j](n);
14794 Roo.log('missing convert for ' + f.name);
14798 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14800 var record = new Record(values, id);
14802 records[i] = record;
14808 totalRecords : totalRecords
14811 // used when loading children.. @see loadDataFromChildren
14812 toLoadData: function(rec)
14814 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14815 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14816 return { data : data, total : data.length };
14821 * Ext JS Library 1.1.1
14822 * Copyright(c) 2006-2007, Ext JS, LLC.
14824 * Originally Released Under LGPL - original licence link has changed is not relivant.
14827 * <script type="text/javascript">
14831 * @class Roo.data.ArrayReader
14832 * @extends Roo.data.DataReader
14833 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14834 * Each element of that Array represents a row of data fields. The
14835 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14836 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14840 var RecordDef = Roo.data.Record.create([
14841 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14842 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14844 var myReader = new Roo.data.ArrayReader({
14845 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14849 * This would consume an Array like this:
14851 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14855 * Create a new JsonReader
14856 * @param {Object} meta Metadata configuration options.
14857 * @param {Object|Array} recordType Either an Array of field definition objects
14859 * @cfg {Array} fields Array of field definition objects
14860 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14861 * as specified to {@link Roo.data.Record#create},
14862 * or an {@link Roo.data.Record} object
14865 * created using {@link Roo.data.Record#create}.
14867 Roo.data.ArrayReader = function(meta, recordType)
14869 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14872 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14875 * Create a data block containing Roo.data.Records from an XML document.
14876 * @param {Object} o An Array of row objects which represents the dataset.
14877 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14878 * a cache of Roo.data.Records.
14880 readRecords : function(o)
14882 var sid = this.meta ? this.meta.id : null;
14883 var recordType = this.recordType, fields = recordType.prototype.fields;
14886 for(var i = 0; i < root.length; i++){
14889 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14890 for(var j = 0, jlen = fields.length; j < jlen; j++){
14891 var f = fields.items[j];
14892 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14893 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14895 values[f.name] = v;
14897 var record = new recordType(values, id);
14899 records[records.length] = record;
14903 totalRecords : records.length
14906 // used when loading children.. @see loadDataFromChildren
14907 toLoadData: function(rec)
14909 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14910 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14921 * @class Roo.bootstrap.ComboBox
14922 * @extends Roo.bootstrap.TriggerField
14923 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14924 * @cfg {Boolean} append (true|false) default false
14925 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14926 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14927 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14928 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14929 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14930 * @cfg {Boolean} animate default true
14931 * @cfg {Boolean} emptyResultText only for touch device
14932 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14933 * @cfg {String} emptyTitle default ''
14934 * @cfg {Number} width fixed with? experimental
14936 * Create a new ComboBox.
14937 * @param {Object} config Configuration options
14939 Roo.bootstrap.ComboBox = function(config){
14940 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14944 * Fires when the dropdown list is expanded
14945 * @param {Roo.bootstrap.ComboBox} combo This combo box
14950 * Fires when the dropdown list is collapsed
14951 * @param {Roo.bootstrap.ComboBox} combo This combo box
14955 * @event beforeselect
14956 * Fires before a list item is selected. Return false to cancel the selection.
14957 * @param {Roo.bootstrap.ComboBox} combo This combo box
14958 * @param {Roo.data.Record} record The data record returned from the underlying store
14959 * @param {Number} index The index of the selected item in the dropdown list
14961 'beforeselect' : true,
14964 * Fires when a list item is selected
14965 * @param {Roo.bootstrap.ComboBox} combo This combo box
14966 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14967 * @param {Number} index The index of the selected item in the dropdown list
14971 * @event beforequery
14972 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14973 * The event object passed has these properties:
14974 * @param {Roo.bootstrap.ComboBox} combo This combo box
14975 * @param {String} query The query
14976 * @param {Boolean} forceAll true to force "all" query
14977 * @param {Boolean} cancel true to cancel the query
14978 * @param {Object} e The query event object
14980 'beforequery': true,
14983 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14984 * @param {Roo.bootstrap.ComboBox} combo This combo box
14989 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14990 * @param {Roo.bootstrap.ComboBox} combo This combo box
14991 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14996 * Fires when the remove value from the combobox array
14997 * @param {Roo.bootstrap.ComboBox} combo This combo box
15001 * @event afterremove
15002 * Fires when the remove value from the combobox array
15003 * @param {Roo.bootstrap.ComboBox} combo This combo box
15005 'afterremove' : true,
15007 * @event specialfilter
15008 * Fires when specialfilter
15009 * @param {Roo.bootstrap.ComboBox} combo This combo box
15011 'specialfilter' : true,
15014 * Fires when tick the element
15015 * @param {Roo.bootstrap.ComboBox} combo This combo box
15019 * @event touchviewdisplay
15020 * Fires when touch view require special display (default is using displayField)
15021 * @param {Roo.bootstrap.ComboBox} combo This combo box
15022 * @param {Object} cfg set html .
15024 'touchviewdisplay' : true
15029 this.tickItems = [];
15031 this.selectedIndex = -1;
15032 if(this.mode == 'local'){
15033 if(config.queryDelay === undefined){
15034 this.queryDelay = 10;
15036 if(config.minChars === undefined){
15042 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15045 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15046 * rendering into an Roo.Editor, defaults to false)
15049 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15050 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15053 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15056 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15057 * the dropdown list (defaults to undefined, with no header element)
15061 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15065 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15067 listWidth: undefined,
15069 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15070 * mode = 'remote' or 'text' if mode = 'local')
15072 displayField: undefined,
15075 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15076 * mode = 'remote' or 'value' if mode = 'local').
15077 * Note: use of a valueField requires the user make a selection
15078 * in order for a value to be mapped.
15080 valueField: undefined,
15082 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15087 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15088 * field's data value (defaults to the underlying DOM element's name)
15090 hiddenName: undefined,
15092 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15096 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15098 selectedClass: 'active',
15101 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15105 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15106 * anchor positions (defaults to 'tl-bl')
15108 listAlign: 'tl-bl?',
15110 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15114 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15115 * query specified by the allQuery config option (defaults to 'query')
15117 triggerAction: 'query',
15119 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15120 * (defaults to 4, does not apply if editable = false)
15124 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15125 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15129 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15130 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15134 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15135 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15139 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15140 * when editable = true (defaults to false)
15142 selectOnFocus:false,
15144 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15146 queryParam: 'query',
15148 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15149 * when mode = 'remote' (defaults to 'Loading...')
15151 loadingText: 'Loading...',
15153 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15157 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15161 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15162 * traditional select (defaults to true)
15166 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15170 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15174 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15175 * listWidth has a higher value)
15179 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15180 * allow the user to set arbitrary text into the field (defaults to false)
15182 forceSelection:false,
15184 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15185 * if typeAhead = true (defaults to 250)
15187 typeAheadDelay : 250,
15189 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15190 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15192 valueNotFoundText : undefined,
15194 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15196 blockFocus : false,
15199 * @cfg {Boolean} disableClear Disable showing of clear button.
15201 disableClear : false,
15203 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15205 alwaysQuery : false,
15208 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15213 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15215 invalidClass : "has-warning",
15218 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15220 validClass : "has-success",
15223 * @cfg {Boolean} specialFilter (true|false) special filter default false
15225 specialFilter : false,
15228 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15230 mobileTouchView : true,
15233 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15235 useNativeIOS : false,
15238 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15240 mobile_restrict_height : false,
15242 ios_options : false,
15254 btnPosition : 'right',
15255 triggerList : true,
15256 showToggleBtn : true,
15258 emptyResultText: 'Empty',
15259 triggerText : 'Select',
15263 // element that contains real text value.. (when hidden is used..)
15265 getAutoCreate : function()
15270 * Render classic select for iso
15273 if(Roo.isIOS && this.useNativeIOS){
15274 cfg = this.getAutoCreateNativeIOS();
15282 if(Roo.isTouch && this.mobileTouchView){
15283 cfg = this.getAutoCreateTouchView();
15290 if(!this.tickable){
15291 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15296 * ComboBox with tickable selections
15299 var align = this.labelAlign || this.parentLabelAlign();
15302 cls : 'form-group roo-combobox-tickable' //input-group
15305 var btn_text_select = '';
15306 var btn_text_done = '';
15307 var btn_text_cancel = '';
15309 if (this.btn_text_show) {
15310 btn_text_select = 'Select';
15311 btn_text_done = 'Done';
15312 btn_text_cancel = 'Cancel';
15317 cls : 'tickable-buttons',
15322 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15323 //html : this.triggerText
15324 html: btn_text_select
15330 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15332 html: btn_text_done
15338 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15340 html: btn_text_cancel
15346 buttons.cn.unshift({
15348 cls: 'roo-select2-search-field-input'
15354 Roo.each(buttons.cn, function(c){
15356 c.cls += ' btn-' + _this.size;
15359 if (_this.disabled) {
15366 style : 'display: contents',
15371 cls: 'form-hidden-field'
15375 cls: 'roo-select2-choices',
15379 cls: 'roo-select2-search-field',
15390 cls: 'roo-select2-container input-group roo-select2-container-multi',
15396 // cls: 'typeahead typeahead-long dropdown-menu',
15397 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15402 if(this.hasFeedback && !this.allowBlank){
15406 cls: 'glyphicon form-control-feedback'
15409 combobox.cn.push(feedback);
15416 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15417 tooltip : 'This field is required'
15419 if (Roo.bootstrap.version == 4) {
15422 style : 'display:none'
15425 if (align ==='left' && this.fieldLabel.length) {
15427 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15434 cls : 'control-label col-form-label',
15435 html : this.fieldLabel
15447 var labelCfg = cfg.cn[1];
15448 var contentCfg = cfg.cn[2];
15451 if(this.indicatorpos == 'right'){
15457 cls : 'control-label col-form-label',
15461 html : this.fieldLabel
15477 labelCfg = cfg.cn[0];
15478 contentCfg = cfg.cn[1];
15482 if(this.labelWidth > 12){
15483 labelCfg.style = "width: " + this.labelWidth + 'px';
15485 if(this.width * 1 > 0){
15486 contentCfg.style = "width: " + this.width + 'px';
15488 if(this.labelWidth < 13 && this.labelmd == 0){
15489 this.labelmd = this.labelWidth;
15492 if(this.labellg > 0){
15493 labelCfg.cls += ' col-lg-' + this.labellg;
15494 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15497 if(this.labelmd > 0){
15498 labelCfg.cls += ' col-md-' + this.labelmd;
15499 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15502 if(this.labelsm > 0){
15503 labelCfg.cls += ' col-sm-' + this.labelsm;
15504 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15507 if(this.labelxs > 0){
15508 labelCfg.cls += ' col-xs-' + this.labelxs;
15509 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15513 } else if ( this.fieldLabel.length) {
15514 // Roo.log(" label");
15519 //cls : 'input-group-addon',
15520 html : this.fieldLabel
15525 if(this.indicatorpos == 'right'){
15529 //cls : 'input-group-addon',
15530 html : this.fieldLabel
15540 // Roo.log(" no label && no align");
15547 ['xs','sm','md','lg'].map(function(size){
15548 if (settings[size]) {
15549 cfg.cls += ' col-' + size + '-' + settings[size];
15557 _initEventsCalled : false,
15560 initEvents: function()
15562 if (this._initEventsCalled) { // as we call render... prevent looping...
15565 this._initEventsCalled = true;
15568 throw "can not find store for combo";
15571 this.indicator = this.indicatorEl();
15573 this.store = Roo.factory(this.store, Roo.data);
15574 this.store.parent = this;
15576 // if we are building from html. then this element is so complex, that we can not really
15577 // use the rendered HTML.
15578 // so we have to trash and replace the previous code.
15579 if (Roo.XComponent.build_from_html) {
15580 // remove this element....
15581 var e = this.el.dom, k=0;
15582 while (e ) { e = e.previousSibling; ++k;}
15587 this.rendered = false;
15589 this.render(this.parent().getChildContainer(true), k);
15592 if(Roo.isIOS && this.useNativeIOS){
15593 this.initIOSView();
15601 if(Roo.isTouch && this.mobileTouchView){
15602 this.initTouchView();
15607 this.initTickableEvents();
15611 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15613 if(this.hiddenName){
15615 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15617 this.hiddenField.dom.value =
15618 this.hiddenValue !== undefined ? this.hiddenValue :
15619 this.value !== undefined ? this.value : '';
15621 // prevent input submission
15622 this.el.dom.removeAttribute('name');
15623 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15628 // this.el.dom.setAttribute('autocomplete', 'off');
15631 var cls = 'x-combo-list';
15633 //this.list = new Roo.Layer({
15634 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15640 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15641 _this.list.setWidth(lw);
15644 this.list.on('mouseover', this.onViewOver, this);
15645 this.list.on('mousemove', this.onViewMove, this);
15646 this.list.on('scroll', this.onViewScroll, this);
15649 this.list.swallowEvent('mousewheel');
15650 this.assetHeight = 0;
15653 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15654 this.assetHeight += this.header.getHeight();
15657 this.innerList = this.list.createChild({cls:cls+'-inner'});
15658 this.innerList.on('mouseover', this.onViewOver, this);
15659 this.innerList.on('mousemove', this.onViewMove, this);
15660 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15662 if(this.allowBlank && !this.pageSize && !this.disableClear){
15663 this.footer = this.list.createChild({cls:cls+'-ft'});
15664 this.pageTb = new Roo.Toolbar(this.footer);
15668 this.footer = this.list.createChild({cls:cls+'-ft'});
15669 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15670 {pageSize: this.pageSize});
15674 if (this.pageTb && this.allowBlank && !this.disableClear) {
15676 this.pageTb.add(new Roo.Toolbar.Fill(), {
15677 cls: 'x-btn-icon x-btn-clear',
15679 handler: function()
15682 _this.clearValue();
15683 _this.onSelect(false, -1);
15688 this.assetHeight += this.footer.getHeight();
15693 this.tpl = Roo.bootstrap.version == 4 ?
15694 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15695 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15698 this.view = new Roo.View(this.list, this.tpl, {
15699 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15701 //this.view.wrapEl.setDisplayed(false);
15702 this.view.on('click', this.onViewClick, this);
15705 this.store.on('beforeload', this.onBeforeLoad, this);
15706 this.store.on('load', this.onLoad, this);
15707 this.store.on('loadexception', this.onLoadException, this);
15709 if(this.resizable){
15710 this.resizer = new Roo.Resizable(this.list, {
15711 pinned:true, handles:'se'
15713 this.resizer.on('resize', function(r, w, h){
15714 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15715 this.listWidth = w;
15716 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15717 this.restrictHeight();
15719 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15722 if(!this.editable){
15723 this.editable = true;
15724 this.setEditable(false);
15729 if (typeof(this.events.add.listeners) != 'undefined') {
15731 this.addicon = this.wrap.createChild(
15732 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15734 this.addicon.on('click', function(e) {
15735 this.fireEvent('add', this);
15738 if (typeof(this.events.edit.listeners) != 'undefined') {
15740 this.editicon = this.wrap.createChild(
15741 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15742 if (this.addicon) {
15743 this.editicon.setStyle('margin-left', '40px');
15745 this.editicon.on('click', function(e) {
15747 // we fire even if inothing is selected..
15748 this.fireEvent('edit', this, this.lastData );
15754 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15755 "up" : function(e){
15756 this.inKeyMode = true;
15760 "down" : function(e){
15761 if(!this.isExpanded()){
15762 this.onTriggerClick();
15764 this.inKeyMode = true;
15769 "enter" : function(e){
15770 // this.onViewClick();
15774 if(this.fireEvent("specialkey", this, e)){
15775 this.onViewClick(false);
15781 "esc" : function(e){
15785 "tab" : function(e){
15788 if(this.fireEvent("specialkey", this, e)){
15789 this.onViewClick(false);
15797 doRelay : function(foo, bar, hname){
15798 if(hname == 'down' || this.scope.isExpanded()){
15799 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15808 this.queryDelay = Math.max(this.queryDelay || 10,
15809 this.mode == 'local' ? 10 : 250);
15812 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15814 if(this.typeAhead){
15815 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15817 if(this.editable !== false){
15818 this.inputEl().on("keyup", this.onKeyUp, this);
15820 if(this.forceSelection){
15821 this.inputEl().on('blur', this.doForce, this);
15825 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15830 initTickableEvents: function()
15834 if(this.hiddenName){
15836 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15838 this.hiddenField.dom.value =
15839 this.hiddenValue !== undefined ? this.hiddenValue :
15840 this.value !== undefined ? this.value : '';
15842 // prevent input submission
15843 this.el.dom.removeAttribute('name');
15844 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15849 // this.list = this.el.select('ul.dropdown-menu',true).first();
15851 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15852 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15853 if(this.triggerList){
15854 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15857 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15858 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15860 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15861 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15863 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15864 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15866 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15867 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15868 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15871 this.cancelBtn.hide();
15876 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15877 _this.list.setWidth(lw);
15880 this.list.on('mouseover', this.onViewOver, this);
15881 this.list.on('mousemove', this.onViewMove, this);
15883 this.list.on('scroll', this.onViewScroll, this);
15886 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15887 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15890 this.view = new Roo.View(this.list, this.tpl, {
15895 selectedClass: this.selectedClass
15898 //this.view.wrapEl.setDisplayed(false);
15899 this.view.on('click', this.onViewClick, this);
15903 this.store.on('beforeload', this.onBeforeLoad, this);
15904 this.store.on('load', this.onLoad, this);
15905 this.store.on('loadexception', this.onLoadException, this);
15908 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15909 "up" : function(e){
15910 this.inKeyMode = true;
15914 "down" : function(e){
15915 this.inKeyMode = true;
15919 "enter" : function(e){
15920 if(this.fireEvent("specialkey", this, e)){
15921 this.onViewClick(false);
15927 "esc" : function(e){
15928 this.onTickableFooterButtonClick(e, false, false);
15931 "tab" : function(e){
15932 this.fireEvent("specialkey", this, e);
15934 this.onTickableFooterButtonClick(e, false, false);
15941 doRelay : function(e, fn, key){
15942 if(this.scope.isExpanded()){
15943 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15952 this.queryDelay = Math.max(this.queryDelay || 10,
15953 this.mode == 'local' ? 10 : 250);
15956 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15958 if(this.typeAhead){
15959 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15962 if(this.editable !== false){
15963 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15966 this.indicator = this.indicatorEl();
15968 if(this.indicator){
15969 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15970 this.indicator.hide();
15975 onDestroy : function(){
15977 this.view.setStore(null);
15978 this.view.el.removeAllListeners();
15979 this.view.el.remove();
15980 this.view.purgeListeners();
15983 this.list.dom.innerHTML = '';
15987 this.store.un('beforeload', this.onBeforeLoad, this);
15988 this.store.un('load', this.onLoad, this);
15989 this.store.un('loadexception', this.onLoadException, this);
15991 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15995 fireKey : function(e){
15996 if(e.isNavKeyPress() && !this.list.isVisible()){
15997 this.fireEvent("specialkey", this, e);
16002 onResize: function(w, h)
16006 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16008 // if(typeof w != 'number'){
16009 // // we do not handle it!?!?
16012 // var tw = this.trigger.getWidth();
16013 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16014 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16016 // this.inputEl().setWidth( this.adjustWidth('input', x));
16018 // //this.trigger.setStyle('left', x+'px');
16020 // if(this.list && this.listWidth === undefined){
16021 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16022 // this.list.setWidth(lw);
16023 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16031 * Allow or prevent the user from directly editing the field text. If false is passed,
16032 * the user will only be able to select from the items defined in the dropdown list. This method
16033 * is the runtime equivalent of setting the 'editable' config option at config time.
16034 * @param {Boolean} value True to allow the user to directly edit the field text
16036 setEditable : function(value){
16037 if(value == this.editable){
16040 this.editable = value;
16042 this.inputEl().dom.setAttribute('readOnly', true);
16043 this.inputEl().on('mousedown', this.onTriggerClick, this);
16044 this.inputEl().addClass('x-combo-noedit');
16046 this.inputEl().dom.setAttribute('readOnly', false);
16047 this.inputEl().un('mousedown', this.onTriggerClick, this);
16048 this.inputEl().removeClass('x-combo-noedit');
16054 onBeforeLoad : function(combo,opts){
16055 if(!this.hasFocus){
16059 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16061 this.restrictHeight();
16062 this.selectedIndex = -1;
16066 onLoad : function(){
16068 this.hasQuery = false;
16070 if(!this.hasFocus){
16074 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16075 this.loading.hide();
16078 if(this.store.getCount() > 0){
16081 this.restrictHeight();
16082 if(this.lastQuery == this.allQuery){
16083 if(this.editable && !this.tickable){
16084 this.inputEl().dom.select();
16088 !this.selectByValue(this.value, true) &&
16091 !this.store.lastOptions ||
16092 typeof(this.store.lastOptions.add) == 'undefined' ||
16093 this.store.lastOptions.add != true
16096 this.select(0, true);
16099 if(this.autoFocus){
16102 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16103 this.taTask.delay(this.typeAheadDelay);
16107 this.onEmptyResults();
16113 onLoadException : function()
16115 this.hasQuery = false;
16117 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16118 this.loading.hide();
16121 if(this.tickable && this.editable){
16126 // only causes errors at present
16127 //Roo.log(this.store.reader.jsonData);
16128 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16130 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16136 onTypeAhead : function(){
16137 if(this.store.getCount() > 0){
16138 var r = this.store.getAt(0);
16139 var newValue = r.data[this.displayField];
16140 var len = newValue.length;
16141 var selStart = this.getRawValue().length;
16143 if(selStart != len){
16144 this.setRawValue(newValue);
16145 this.selectText(selStart, newValue.length);
16151 onSelect : function(record, index){
16153 if(this.fireEvent('beforeselect', this, record, index) !== false){
16155 this.setFromData(index > -1 ? record.data : false);
16158 this.fireEvent('select', this, record, index);
16163 * Returns the currently selected field value or empty string if no value is set.
16164 * @return {String} value The selected value
16166 getValue : function()
16168 if(Roo.isIOS && this.useNativeIOS){
16169 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16173 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16176 if(this.valueField){
16177 return typeof this.value != 'undefined' ? this.value : '';
16179 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16183 getRawValue : function()
16185 if(Roo.isIOS && this.useNativeIOS){
16186 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16189 var v = this.inputEl().getValue();
16195 * Clears any text/value currently set in the field
16197 clearValue : function(){
16199 if(this.hiddenField){
16200 this.hiddenField.dom.value = '';
16203 this.setRawValue('');
16204 this.lastSelectionText = '';
16205 this.lastData = false;
16207 var close = this.closeTriggerEl();
16218 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16219 * will be displayed in the field. If the value does not match the data value of an existing item,
16220 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16221 * Otherwise the field will be blank (although the value will still be set).
16222 * @param {String} value The value to match
16224 setValue : function(v)
16226 if(Roo.isIOS && this.useNativeIOS){
16227 this.setIOSValue(v);
16237 if(this.valueField){
16238 var r = this.findRecord(this.valueField, v);
16240 text = r.data[this.displayField];
16241 }else if(this.valueNotFoundText !== undefined){
16242 text = this.valueNotFoundText;
16245 this.lastSelectionText = text;
16246 if(this.hiddenField){
16247 this.hiddenField.dom.value = v;
16249 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16252 var close = this.closeTriggerEl();
16255 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16261 * @property {Object} the last set data for the element
16266 * Sets the value of the field based on a object which is related to the record format for the store.
16267 * @param {Object} value the value to set as. or false on reset?
16269 setFromData : function(o){
16276 var dv = ''; // display value
16277 var vv = ''; // value value..
16279 if (this.displayField) {
16280 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16282 // this is an error condition!!!
16283 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16286 if(this.valueField){
16287 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16290 var close = this.closeTriggerEl();
16293 if(dv.length || vv * 1 > 0){
16295 this.blockFocus=true;
16301 if(this.hiddenField){
16302 this.hiddenField.dom.value = vv;
16304 this.lastSelectionText = dv;
16305 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16309 // no hidden field.. - we store the value in 'value', but still display
16310 // display field!!!!
16311 this.lastSelectionText = dv;
16312 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16319 reset : function(){
16320 // overridden so that last data is reset..
16327 this.setValue(this.originalValue);
16328 //this.clearInvalid();
16329 this.lastData = false;
16331 this.view.clearSelections();
16337 findRecord : function(prop, value){
16339 if(this.store.getCount() > 0){
16340 this.store.each(function(r){
16341 if(r.data[prop] == value){
16351 getName: function()
16353 // returns hidden if it's set..
16354 if (!this.rendered) {return ''};
16355 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16359 onViewMove : function(e, t){
16360 this.inKeyMode = false;
16364 onViewOver : function(e, t){
16365 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16368 var item = this.view.findItemFromChild(t);
16371 var index = this.view.indexOf(item);
16372 this.select(index, false);
16377 onViewClick : function(view, doFocus, el, e)
16379 var index = this.view.getSelectedIndexes()[0];
16381 var r = this.store.getAt(index);
16385 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16392 Roo.each(this.tickItems, function(v,k){
16394 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16396 _this.tickItems.splice(k, 1);
16398 if(typeof(e) == 'undefined' && view == false){
16399 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16411 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16412 this.tickItems.push(r.data);
16415 if(typeof(e) == 'undefined' && view == false){
16416 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16423 this.onSelect(r, index);
16425 if(doFocus !== false && !this.blockFocus){
16426 this.inputEl().focus();
16431 restrictHeight : function(){
16432 //this.innerList.dom.style.height = '';
16433 //var inner = this.innerList.dom;
16434 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16435 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16436 //this.list.beginUpdate();
16437 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16438 this.list.alignTo(this.inputEl(), this.listAlign);
16439 this.list.alignTo(this.inputEl(), this.listAlign);
16440 //this.list.endUpdate();
16444 onEmptyResults : function(){
16446 if(this.tickable && this.editable){
16447 this.hasFocus = false;
16448 this.restrictHeight();
16456 * Returns true if the dropdown list is expanded, else false.
16458 isExpanded : function(){
16459 return this.list.isVisible();
16463 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16464 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16465 * @param {String} value The data value of the item to select
16466 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16467 * selected item if it is not currently in view (defaults to true)
16468 * @return {Boolean} True if the value matched an item in the list, else false
16470 selectByValue : function(v, scrollIntoView){
16471 if(v !== undefined && v !== null){
16472 var r = this.findRecord(this.valueField || this.displayField, v);
16474 this.select(this.store.indexOf(r), scrollIntoView);
16482 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16483 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16484 * @param {Number} index The zero-based index of the list item to select
16485 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16486 * selected item if it is not currently in view (defaults to true)
16488 select : function(index, scrollIntoView){
16489 this.selectedIndex = index;
16490 this.view.select(index);
16491 if(scrollIntoView !== false){
16492 var el = this.view.getNode(index);
16494 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16497 this.list.scrollChildIntoView(el, false);
16503 selectNext : function(){
16504 var ct = this.store.getCount();
16506 if(this.selectedIndex == -1){
16508 }else if(this.selectedIndex < ct-1){
16509 this.select(this.selectedIndex+1);
16515 selectPrev : function(){
16516 var ct = this.store.getCount();
16518 if(this.selectedIndex == -1){
16520 }else if(this.selectedIndex != 0){
16521 this.select(this.selectedIndex-1);
16527 onKeyUp : function(e){
16528 if(this.editable !== false && !e.isSpecialKey()){
16529 this.lastKey = e.getKey();
16530 this.dqTask.delay(this.queryDelay);
16535 validateBlur : function(){
16536 return !this.list || !this.list.isVisible();
16540 initQuery : function(){
16542 var v = this.getRawValue();
16544 if(this.tickable && this.editable){
16545 v = this.tickableInputEl().getValue();
16552 doForce : function(){
16553 if(this.inputEl().dom.value.length > 0){
16554 this.inputEl().dom.value =
16555 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16561 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16562 * query allowing the query action to be canceled if needed.
16563 * @param {String} query The SQL query to execute
16564 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16565 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16566 * saved in the current store (defaults to false)
16568 doQuery : function(q, forceAll){
16570 if(q === undefined || q === null){
16575 forceAll: forceAll,
16579 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16584 forceAll = qe.forceAll;
16585 if(forceAll === true || (q.length >= this.minChars)){
16587 this.hasQuery = true;
16589 if(this.lastQuery != q || this.alwaysQuery){
16590 this.lastQuery = q;
16591 if(this.mode == 'local'){
16592 this.selectedIndex = -1;
16594 this.store.clearFilter();
16597 if(this.specialFilter){
16598 this.fireEvent('specialfilter', this);
16603 this.store.filter(this.displayField, q);
16606 this.store.fireEvent("datachanged", this.store);
16613 this.store.baseParams[this.queryParam] = q;
16615 var options = {params : this.getParams(q)};
16618 options.add = true;
16619 options.params.start = this.page * this.pageSize;
16622 this.store.load(options);
16625 * this code will make the page width larger, at the beginning, the list not align correctly,
16626 * we should expand the list on onLoad
16627 * so command out it
16632 this.selectedIndex = -1;
16637 this.loadNext = false;
16641 getParams : function(q){
16643 //p[this.queryParam] = q;
16647 p.limit = this.pageSize;
16653 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16655 collapse : function(){
16656 if(!this.isExpanded()){
16662 this.hasFocus = false;
16666 this.cancelBtn.hide();
16667 this.trigger.show();
16670 this.tickableInputEl().dom.value = '';
16671 this.tickableInputEl().blur();
16676 Roo.get(document).un('mousedown', this.collapseIf, this);
16677 Roo.get(document).un('mousewheel', this.collapseIf, this);
16678 if (!this.editable) {
16679 Roo.get(document).un('keydown', this.listKeyPress, this);
16681 this.fireEvent('collapse', this);
16687 collapseIf : function(e){
16688 var in_combo = e.within(this.el);
16689 var in_list = e.within(this.list);
16690 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16692 if (in_combo || in_list || is_list) {
16693 //e.stopPropagation();
16698 this.onTickableFooterButtonClick(e, false, false);
16706 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16708 expand : function(){
16710 if(this.isExpanded() || !this.hasFocus){
16714 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16715 this.list.setWidth(lw);
16721 this.restrictHeight();
16725 this.tickItems = Roo.apply([], this.item);
16728 this.cancelBtn.show();
16729 this.trigger.hide();
16732 this.tickableInputEl().focus();
16737 Roo.get(document).on('mousedown', this.collapseIf, this);
16738 Roo.get(document).on('mousewheel', this.collapseIf, this);
16739 if (!this.editable) {
16740 Roo.get(document).on('keydown', this.listKeyPress, this);
16743 this.fireEvent('expand', this);
16747 // Implements the default empty TriggerField.onTriggerClick function
16748 onTriggerClick : function(e)
16750 Roo.log('trigger click');
16752 if(this.disabled || !this.triggerList){
16757 this.loadNext = false;
16759 if(this.isExpanded()){
16761 if (!this.blockFocus) {
16762 this.inputEl().focus();
16766 this.hasFocus = true;
16767 if(this.triggerAction == 'all') {
16768 this.doQuery(this.allQuery, true);
16770 this.doQuery(this.getRawValue());
16772 if (!this.blockFocus) {
16773 this.inputEl().focus();
16778 onTickableTriggerClick : function(e)
16785 this.loadNext = false;
16786 this.hasFocus = true;
16788 if(this.triggerAction == 'all') {
16789 this.doQuery(this.allQuery, true);
16791 this.doQuery(this.getRawValue());
16795 onSearchFieldClick : function(e)
16797 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16798 this.onTickableFooterButtonClick(e, false, false);
16802 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16807 this.loadNext = false;
16808 this.hasFocus = true;
16810 if(this.triggerAction == 'all') {
16811 this.doQuery(this.allQuery, true);
16813 this.doQuery(this.getRawValue());
16817 listKeyPress : function(e)
16819 //Roo.log('listkeypress');
16820 // scroll to first matching element based on key pres..
16821 if (e.isSpecialKey()) {
16824 var k = String.fromCharCode(e.getKey()).toUpperCase();
16827 var csel = this.view.getSelectedNodes();
16828 var cselitem = false;
16830 var ix = this.view.indexOf(csel[0]);
16831 cselitem = this.store.getAt(ix);
16832 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16838 this.store.each(function(v) {
16840 // start at existing selection.
16841 if (cselitem.id == v.id) {
16847 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16848 match = this.store.indexOf(v);
16854 if (match === false) {
16855 return true; // no more action?
16858 this.view.select(match);
16859 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16860 sn.scrollIntoView(sn.dom.parentNode, false);
16863 onViewScroll : function(e, t){
16865 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){
16869 this.hasQuery = true;
16871 this.loading = this.list.select('.loading', true).first();
16873 if(this.loading === null){
16874 this.list.createChild({
16876 cls: 'loading roo-select2-more-results roo-select2-active',
16877 html: 'Loading more results...'
16880 this.loading = this.list.select('.loading', true).first();
16882 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16884 this.loading.hide();
16887 this.loading.show();
16892 this.loadNext = true;
16894 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16899 addItem : function(o)
16901 var dv = ''; // display value
16903 if (this.displayField) {
16904 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16906 // this is an error condition!!!
16907 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16914 var choice = this.choices.createChild({
16916 cls: 'roo-select2-search-choice',
16925 cls: 'roo-select2-search-choice-close fa fa-times',
16930 }, this.searchField);
16932 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16934 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16942 this.inputEl().dom.value = '';
16947 onRemoveItem : function(e, _self, o)
16949 e.preventDefault();
16951 this.lastItem = Roo.apply([], this.item);
16953 var index = this.item.indexOf(o.data) * 1;
16956 Roo.log('not this item?!');
16960 this.item.splice(index, 1);
16965 this.fireEvent('remove', this, e);
16971 syncValue : function()
16973 if(!this.item.length){
16980 Roo.each(this.item, function(i){
16981 if(_this.valueField){
16982 value.push(i[_this.valueField]);
16989 this.value = value.join(',');
16991 if(this.hiddenField){
16992 this.hiddenField.dom.value = this.value;
16995 this.store.fireEvent("datachanged", this.store);
17000 clearItem : function()
17002 if(!this.multiple){
17008 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17016 if(this.tickable && !Roo.isTouch){
17017 this.view.refresh();
17021 inputEl: function ()
17023 if(Roo.isIOS && this.useNativeIOS){
17024 return this.el.select('select.roo-ios-select', true).first();
17027 if(Roo.isTouch && this.mobileTouchView){
17028 return this.el.select('input.form-control',true).first();
17032 return this.searchField;
17035 return this.el.select('input.form-control',true).first();
17038 onTickableFooterButtonClick : function(e, btn, el)
17040 e.preventDefault();
17042 this.lastItem = Roo.apply([], this.item);
17044 if(btn && btn.name == 'cancel'){
17045 this.tickItems = Roo.apply([], this.item);
17054 Roo.each(this.tickItems, function(o){
17062 validate : function()
17064 if(this.getVisibilityEl().hasClass('hidden')){
17068 var v = this.getRawValue();
17071 v = this.getValue();
17074 if(this.disabled || this.allowBlank || v.length){
17079 this.markInvalid();
17083 tickableInputEl : function()
17085 if(!this.tickable || !this.editable){
17086 return this.inputEl();
17089 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17093 getAutoCreateTouchView : function()
17098 cls: 'form-group' //input-group
17104 type : this.inputType,
17105 cls : 'form-control x-combo-noedit',
17106 autocomplete: 'new-password',
17107 placeholder : this.placeholder || '',
17112 input.name = this.name;
17116 input.cls += ' input-' + this.size;
17119 if (this.disabled) {
17120 input.disabled = true;
17124 cls : 'roo-combobox-wrap',
17131 inputblock.cls += ' input-group';
17133 inputblock.cn.unshift({
17135 cls : 'input-group-addon input-group-prepend input-group-text',
17140 if(this.removable && !this.multiple){
17141 inputblock.cls += ' roo-removable';
17143 inputblock.cn.push({
17146 cls : 'roo-combo-removable-btn close'
17150 if(this.hasFeedback && !this.allowBlank){
17152 inputblock.cls += ' has-feedback';
17154 inputblock.cn.push({
17156 cls: 'glyphicon form-control-feedback'
17163 inputblock.cls += (this.before) ? '' : ' input-group';
17165 inputblock.cn.push({
17167 cls : 'input-group-addon input-group-append input-group-text',
17173 var ibwrap = inputblock;
17178 cls: 'roo-select2-choices',
17182 cls: 'roo-select2-search-field',
17195 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17200 cls: 'form-hidden-field'
17206 if(!this.multiple && this.showToggleBtn){
17212 if (this.caret != false) {
17215 cls: 'fa fa-' + this.caret
17222 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17224 Roo.bootstrap.version == 3 ? caret : '',
17227 cls: 'combobox-clear',
17241 combobox.cls += ' roo-select2-container-multi';
17244 var align = this.labelAlign || this.parentLabelAlign();
17246 if (align ==='left' && this.fieldLabel.length) {
17251 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17252 tooltip : 'This field is required'
17256 cls : 'control-label col-form-label',
17257 html : this.fieldLabel
17261 cls : 'roo-combobox-wrap ',
17268 var labelCfg = cfg.cn[1];
17269 var contentCfg = cfg.cn[2];
17272 if(this.indicatorpos == 'right'){
17277 cls : 'control-label col-form-label',
17281 html : this.fieldLabel
17285 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17286 tooltip : 'This field is required'
17291 cls : "roo-combobox-wrap ",
17299 labelCfg = cfg.cn[0];
17300 contentCfg = cfg.cn[1];
17305 if(this.labelWidth > 12){
17306 labelCfg.style = "width: " + this.labelWidth + 'px';
17309 if(this.labelWidth < 13 && this.labelmd == 0){
17310 this.labelmd = this.labelWidth;
17313 if(this.labellg > 0){
17314 labelCfg.cls += ' col-lg-' + this.labellg;
17315 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17318 if(this.labelmd > 0){
17319 labelCfg.cls += ' col-md-' + this.labelmd;
17320 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17323 if(this.labelsm > 0){
17324 labelCfg.cls += ' col-sm-' + this.labelsm;
17325 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17328 if(this.labelxs > 0){
17329 labelCfg.cls += ' col-xs-' + this.labelxs;
17330 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17334 } else if ( this.fieldLabel.length) {
17338 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17339 tooltip : 'This field is required'
17343 cls : 'control-label',
17344 html : this.fieldLabel
17355 if(this.indicatorpos == 'right'){
17359 cls : 'control-label',
17360 html : this.fieldLabel,
17364 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17365 tooltip : 'This field is required'
17382 var settings = this;
17384 ['xs','sm','md','lg'].map(function(size){
17385 if (settings[size]) {
17386 cfg.cls += ' col-' + size + '-' + settings[size];
17393 initTouchView : function()
17395 this.renderTouchView();
17397 this.touchViewEl.on('scroll', function(){
17398 this.el.dom.scrollTop = 0;
17401 this.originalValue = this.getValue();
17403 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17405 this.inputEl().on("click", this.showTouchView, this);
17406 if (this.triggerEl) {
17407 this.triggerEl.on("click", this.showTouchView, this);
17411 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17412 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17414 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17416 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17417 this.store.on('load', this.onTouchViewLoad, this);
17418 this.store.on('loadexception', this.onTouchViewLoadException, this);
17420 if(this.hiddenName){
17422 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17424 this.hiddenField.dom.value =
17425 this.hiddenValue !== undefined ? this.hiddenValue :
17426 this.value !== undefined ? this.value : '';
17428 this.el.dom.removeAttribute('name');
17429 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17433 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17434 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17437 if(this.removable && !this.multiple){
17438 var close = this.closeTriggerEl();
17440 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17441 close.on('click', this.removeBtnClick, this, close);
17445 * fix the bug in Safari iOS8
17447 this.inputEl().on("focus", function(e){
17448 document.activeElement.blur();
17451 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17458 renderTouchView : function()
17460 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17461 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17463 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17464 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17466 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17467 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17468 this.touchViewBodyEl.setStyle('overflow', 'auto');
17470 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17471 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17473 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17474 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17478 showTouchView : function()
17484 this.touchViewHeaderEl.hide();
17486 if(this.modalTitle.length){
17487 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17488 this.touchViewHeaderEl.show();
17491 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17492 this.touchViewEl.show();
17494 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17496 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17497 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17499 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17501 if(this.modalTitle.length){
17502 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17505 this.touchViewBodyEl.setHeight(bodyHeight);
17509 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17511 this.touchViewEl.addClass(['in','show']);
17514 if(this._touchViewMask){
17515 Roo.get(document.body).addClass("x-body-masked");
17516 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17517 this._touchViewMask.setStyle('z-index', 10000);
17518 this._touchViewMask.addClass('show');
17521 this.doTouchViewQuery();
17525 hideTouchView : function()
17527 this.touchViewEl.removeClass(['in','show']);
17531 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17533 this.touchViewEl.setStyle('display', 'none');
17536 if(this._touchViewMask){
17537 this._touchViewMask.removeClass('show');
17538 Roo.get(document.body).removeClass("x-body-masked");
17542 setTouchViewValue : function()
17549 Roo.each(this.tickItems, function(o){
17554 this.hideTouchView();
17557 doTouchViewQuery : function()
17566 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17570 if(!this.alwaysQuery || this.mode == 'local'){
17571 this.onTouchViewLoad();
17578 onTouchViewBeforeLoad : function(combo,opts)
17584 onTouchViewLoad : function()
17586 if(this.store.getCount() < 1){
17587 this.onTouchViewEmptyResults();
17591 this.clearTouchView();
17593 var rawValue = this.getRawValue();
17595 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17597 this.tickItems = [];
17599 this.store.data.each(function(d, rowIndex){
17600 var row = this.touchViewListGroup.createChild(template);
17602 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17603 row.addClass(d.data.cls);
17606 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17609 html : d.data[this.displayField]
17612 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17613 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17616 row.removeClass('selected');
17617 if(!this.multiple && this.valueField &&
17618 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17621 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17622 row.addClass('selected');
17625 if(this.multiple && this.valueField &&
17626 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17630 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17631 this.tickItems.push(d.data);
17634 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17638 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17640 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17642 if(this.modalTitle.length){
17643 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17646 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17648 if(this.mobile_restrict_height && listHeight < bodyHeight){
17649 this.touchViewBodyEl.setHeight(listHeight);
17654 if(firstChecked && listHeight > bodyHeight){
17655 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17660 onTouchViewLoadException : function()
17662 this.hideTouchView();
17665 onTouchViewEmptyResults : function()
17667 this.clearTouchView();
17669 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17671 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17675 clearTouchView : function()
17677 this.touchViewListGroup.dom.innerHTML = '';
17680 onTouchViewClick : function(e, el, o)
17682 e.preventDefault();
17685 var rowIndex = o.rowIndex;
17687 var r = this.store.getAt(rowIndex);
17689 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17691 if(!this.multiple){
17692 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17693 c.dom.removeAttribute('checked');
17696 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17698 this.setFromData(r.data);
17700 var close = this.closeTriggerEl();
17706 this.hideTouchView();
17708 this.fireEvent('select', this, r, rowIndex);
17713 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17714 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17715 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17719 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17720 this.addItem(r.data);
17721 this.tickItems.push(r.data);
17725 getAutoCreateNativeIOS : function()
17728 cls: 'form-group' //input-group,
17733 cls : 'roo-ios-select'
17737 combobox.name = this.name;
17740 if (this.disabled) {
17741 combobox.disabled = true;
17744 var settings = this;
17746 ['xs','sm','md','lg'].map(function(size){
17747 if (settings[size]) {
17748 cfg.cls += ' col-' + size + '-' + settings[size];
17758 initIOSView : function()
17760 this.store.on('load', this.onIOSViewLoad, this);
17765 onIOSViewLoad : function()
17767 if(this.store.getCount() < 1){
17771 this.clearIOSView();
17773 if(this.allowBlank) {
17775 var default_text = '-- SELECT --';
17777 if(this.placeholder.length){
17778 default_text = this.placeholder;
17781 if(this.emptyTitle.length){
17782 default_text += ' - ' + this.emptyTitle + ' -';
17785 var opt = this.inputEl().createChild({
17788 html : default_text
17792 o[this.valueField] = 0;
17793 o[this.displayField] = default_text;
17795 this.ios_options.push({
17802 this.store.data.each(function(d, rowIndex){
17806 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17807 html = d.data[this.displayField];
17812 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17813 value = d.data[this.valueField];
17822 if(this.value == d.data[this.valueField]){
17823 option['selected'] = true;
17826 var opt = this.inputEl().createChild(option);
17828 this.ios_options.push({
17835 this.inputEl().on('change', function(){
17836 this.fireEvent('select', this);
17841 clearIOSView: function()
17843 this.inputEl().dom.innerHTML = '';
17845 this.ios_options = [];
17848 setIOSValue: function(v)
17852 if(!this.ios_options){
17856 Roo.each(this.ios_options, function(opts){
17858 opts.el.dom.removeAttribute('selected');
17860 if(opts.data[this.valueField] != v){
17864 opts.el.dom.setAttribute('selected', true);
17870 * @cfg {Boolean} grow
17874 * @cfg {Number} growMin
17878 * @cfg {Number} growMax
17887 Roo.apply(Roo.bootstrap.ComboBox, {
17891 cls: 'modal-header',
17913 cls: 'list-group-item',
17917 cls: 'roo-combobox-list-group-item-value'
17921 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17935 listItemCheckbox : {
17937 cls: 'list-group-item',
17941 cls: 'roo-combobox-list-group-item-value'
17945 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17961 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17966 cls: 'modal-footer',
17974 cls: 'col-xs-6 text-left',
17977 cls: 'btn btn-danger roo-touch-view-cancel',
17983 cls: 'col-xs-6 text-right',
17986 cls: 'btn btn-success roo-touch-view-ok',
17997 Roo.apply(Roo.bootstrap.ComboBox, {
17999 touchViewTemplate : {
18001 cls: 'modal fade roo-combobox-touch-view',
18005 cls: 'modal-dialog',
18006 style : 'position:fixed', // we have to fix position....
18010 cls: 'modal-content',
18012 Roo.bootstrap.ComboBox.header,
18013 Roo.bootstrap.ComboBox.body,
18014 Roo.bootstrap.ComboBox.footer
18023 * Ext JS Library 1.1.1
18024 * Copyright(c) 2006-2007, Ext JS, LLC.
18026 * Originally Released Under LGPL - original licence link has changed is not relivant.
18029 * <script type="text/javascript">
18034 * @extends Roo.util.Observable
18035 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18036 * This class also supports single and multi selection modes. <br>
18037 * Create a data model bound view:
18039 var store = new Roo.data.Store(...);
18041 var view = new Roo.View({
18043 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18045 singleSelect: true,
18046 selectedClass: "ydataview-selected",
18050 // listen for node click?
18051 view.on("click", function(vw, index, node, e){
18052 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18056 dataModel.load("foobar.xml");
18058 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18060 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18061 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18063 * Note: old style constructor is still suported (container, template, config)
18066 * Create a new View
18067 * @param {Object} config The config object
18070 Roo.View = function(config, depreciated_tpl, depreciated_config){
18072 this.parent = false;
18074 if (typeof(depreciated_tpl) == 'undefined') {
18075 // new way.. - universal constructor.
18076 Roo.apply(this, config);
18077 this.el = Roo.get(this.el);
18080 this.el = Roo.get(config);
18081 this.tpl = depreciated_tpl;
18082 Roo.apply(this, depreciated_config);
18084 this.wrapEl = this.el.wrap().wrap();
18085 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18088 if(typeof(this.tpl) == "string"){
18089 this.tpl = new Roo.Template(this.tpl);
18091 // support xtype ctors..
18092 this.tpl = new Roo.factory(this.tpl, Roo);
18096 this.tpl.compile();
18101 * @event beforeclick
18102 * Fires before a click is processed. Returns false to cancel the default action.
18103 * @param {Roo.View} this
18104 * @param {Number} index The index of the target node
18105 * @param {HTMLElement} node The target node
18106 * @param {Roo.EventObject} e The raw event object
18108 "beforeclick" : true,
18111 * Fires when a template node is clicked.
18112 * @param {Roo.View} this
18113 * @param {Number} index The index of the target node
18114 * @param {HTMLElement} node The target node
18115 * @param {Roo.EventObject} e The raw event object
18120 * Fires when a template node is double clicked.
18121 * @param {Roo.View} this
18122 * @param {Number} index The index of the target node
18123 * @param {HTMLElement} node The target node
18124 * @param {Roo.EventObject} e The raw event object
18128 * @event contextmenu
18129 * Fires when a template node is right clicked.
18130 * @param {Roo.View} this
18131 * @param {Number} index The index of the target node
18132 * @param {HTMLElement} node The target node
18133 * @param {Roo.EventObject} e The raw event object
18135 "contextmenu" : true,
18137 * @event selectionchange
18138 * Fires when the selected nodes change.
18139 * @param {Roo.View} this
18140 * @param {Array} selections Array of the selected nodes
18142 "selectionchange" : true,
18145 * @event beforeselect
18146 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18147 * @param {Roo.View} this
18148 * @param {HTMLElement} node The node to be selected
18149 * @param {Array} selections Array of currently selected nodes
18151 "beforeselect" : true,
18153 * @event preparedata
18154 * Fires on every row to render, to allow you to change the data.
18155 * @param {Roo.View} this
18156 * @param {Object} data to be rendered (change this)
18158 "preparedata" : true
18166 "click": this.onClick,
18167 "dblclick": this.onDblClick,
18168 "contextmenu": this.onContextMenu,
18172 this.selections = [];
18174 this.cmp = new Roo.CompositeElementLite([]);
18176 this.store = Roo.factory(this.store, Roo.data);
18177 this.setStore(this.store, true);
18180 if ( this.footer && this.footer.xtype) {
18182 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18184 this.footer.dataSource = this.store;
18185 this.footer.container = fctr;
18186 this.footer = Roo.factory(this.footer, Roo);
18187 fctr.insertFirst(this.el);
18189 // this is a bit insane - as the paging toolbar seems to detach the el..
18190 // dom.parentNode.parentNode.parentNode
18191 // they get detached?
18195 Roo.View.superclass.constructor.call(this);
18200 Roo.extend(Roo.View, Roo.util.Observable, {
18203 * @cfg {Roo.data.Store} store Data store to load data from.
18208 * @cfg {String|Roo.Element} el The container element.
18213 * @cfg {String|Roo.Template} tpl The template used by this View
18217 * @cfg {String} dataName the named area of the template to use as the data area
18218 * Works with domtemplates roo-name="name"
18222 * @cfg {String} selectedClass The css class to add to selected nodes
18224 selectedClass : "x-view-selected",
18226 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18231 * @cfg {String} text to display on mask (default Loading)
18235 * @cfg {Boolean} multiSelect Allow multiple selection
18237 multiSelect : false,
18239 * @cfg {Boolean} singleSelect Allow single selection
18241 singleSelect: false,
18244 * @cfg {Boolean} toggleSelect - selecting
18246 toggleSelect : false,
18249 * @cfg {Boolean} tickable - selecting
18254 * Returns the element this view is bound to.
18255 * @return {Roo.Element}
18257 getEl : function(){
18258 return this.wrapEl;
18264 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18266 refresh : function(){
18267 //Roo.log('refresh');
18270 // if we are using something like 'domtemplate', then
18271 // the what gets used is:
18272 // t.applySubtemplate(NAME, data, wrapping data..)
18273 // the outer template then get' applied with
18274 // the store 'extra data'
18275 // and the body get's added to the
18276 // roo-name="data" node?
18277 // <span class='roo-tpl-{name}'></span> ?????
18281 this.clearSelections();
18282 this.el.update("");
18284 var records = this.store.getRange();
18285 if(records.length < 1) {
18287 // is this valid?? = should it render a template??
18289 this.el.update(this.emptyText);
18293 if (this.dataName) {
18294 this.el.update(t.apply(this.store.meta)); //????
18295 el = this.el.child('.roo-tpl-' + this.dataName);
18298 for(var i = 0, len = records.length; i < len; i++){
18299 var data = this.prepareData(records[i].data, i, records[i]);
18300 this.fireEvent("preparedata", this, data, i, records[i]);
18302 var d = Roo.apply({}, data);
18305 Roo.apply(d, {'roo-id' : Roo.id()});
18309 Roo.each(this.parent.item, function(item){
18310 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18313 Roo.apply(d, {'roo-data-checked' : 'checked'});
18317 html[html.length] = Roo.util.Format.trim(
18319 t.applySubtemplate(this.dataName, d, this.store.meta) :
18326 el.update(html.join(""));
18327 this.nodes = el.dom.childNodes;
18328 this.updateIndexes(0);
18333 * Function to override to reformat the data that is sent to
18334 * the template for each node.
18335 * DEPRICATED - use the preparedata event handler.
18336 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18337 * a JSON object for an UpdateManager bound view).
18339 prepareData : function(data, index, record)
18341 this.fireEvent("preparedata", this, data, index, record);
18345 onUpdate : function(ds, record){
18346 // Roo.log('on update');
18347 this.clearSelections();
18348 var index = this.store.indexOf(record);
18349 var n = this.nodes[index];
18350 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18351 n.parentNode.removeChild(n);
18352 this.updateIndexes(index, index);
18358 onAdd : function(ds, records, index)
18360 //Roo.log(['on Add', ds, records, index] );
18361 this.clearSelections();
18362 if(this.nodes.length == 0){
18366 var n = this.nodes[index];
18367 for(var i = 0, len = records.length; i < len; i++){
18368 var d = this.prepareData(records[i].data, i, records[i]);
18370 this.tpl.insertBefore(n, d);
18373 this.tpl.append(this.el, d);
18376 this.updateIndexes(index);
18379 onRemove : function(ds, record, index){
18380 // Roo.log('onRemove');
18381 this.clearSelections();
18382 var el = this.dataName ?
18383 this.el.child('.roo-tpl-' + this.dataName) :
18386 el.dom.removeChild(this.nodes[index]);
18387 this.updateIndexes(index);
18391 * Refresh an individual node.
18392 * @param {Number} index
18394 refreshNode : function(index){
18395 this.onUpdate(this.store, this.store.getAt(index));
18398 updateIndexes : function(startIndex, endIndex){
18399 var ns = this.nodes;
18400 startIndex = startIndex || 0;
18401 endIndex = endIndex || ns.length - 1;
18402 for(var i = startIndex; i <= endIndex; i++){
18403 ns[i].nodeIndex = i;
18408 * Changes the data store this view uses and refresh the view.
18409 * @param {Store} store
18411 setStore : function(store, initial){
18412 if(!initial && this.store){
18413 this.store.un("datachanged", this.refresh);
18414 this.store.un("add", this.onAdd);
18415 this.store.un("remove", this.onRemove);
18416 this.store.un("update", this.onUpdate);
18417 this.store.un("clear", this.refresh);
18418 this.store.un("beforeload", this.onBeforeLoad);
18419 this.store.un("load", this.onLoad);
18420 this.store.un("loadexception", this.onLoad);
18424 store.on("datachanged", this.refresh, this);
18425 store.on("add", this.onAdd, this);
18426 store.on("remove", this.onRemove, this);
18427 store.on("update", this.onUpdate, this);
18428 store.on("clear", this.refresh, this);
18429 store.on("beforeload", this.onBeforeLoad, this);
18430 store.on("load", this.onLoad, this);
18431 store.on("loadexception", this.onLoad, this);
18439 * onbeforeLoad - masks the loading area.
18442 onBeforeLoad : function(store,opts)
18444 //Roo.log('onBeforeLoad');
18446 this.el.update("");
18448 this.el.mask(this.mask ? this.mask : "Loading" );
18450 onLoad : function ()
18457 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18458 * @param {HTMLElement} node
18459 * @return {HTMLElement} The template node
18461 findItemFromChild : function(node){
18462 var el = this.dataName ?
18463 this.el.child('.roo-tpl-' + this.dataName,true) :
18466 if(!node || node.parentNode == el){
18469 var p = node.parentNode;
18470 while(p && p != el){
18471 if(p.parentNode == el){
18480 onClick : function(e){
18481 var item = this.findItemFromChild(e.getTarget());
18483 var index = this.indexOf(item);
18484 if(this.onItemClick(item, index, e) !== false){
18485 this.fireEvent("click", this, index, item, e);
18488 this.clearSelections();
18493 onContextMenu : function(e){
18494 var item = this.findItemFromChild(e.getTarget());
18496 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18501 onDblClick : function(e){
18502 var item = this.findItemFromChild(e.getTarget());
18504 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18508 onItemClick : function(item, index, e)
18510 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18513 if (this.toggleSelect) {
18514 var m = this.isSelected(item) ? 'unselect' : 'select';
18517 _t[m](item, true, false);
18520 if(this.multiSelect || this.singleSelect){
18521 if(this.multiSelect && e.shiftKey && this.lastSelection){
18522 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18524 this.select(item, this.multiSelect && e.ctrlKey);
18525 this.lastSelection = item;
18528 if(!this.tickable){
18529 e.preventDefault();
18537 * Get the number of selected nodes.
18540 getSelectionCount : function(){
18541 return this.selections.length;
18545 * Get the currently selected nodes.
18546 * @return {Array} An array of HTMLElements
18548 getSelectedNodes : function(){
18549 return this.selections;
18553 * Get the indexes of the selected nodes.
18556 getSelectedIndexes : function(){
18557 var indexes = [], s = this.selections;
18558 for(var i = 0, len = s.length; i < len; i++){
18559 indexes.push(s[i].nodeIndex);
18565 * Clear all selections
18566 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18568 clearSelections : function(suppressEvent){
18569 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18570 this.cmp.elements = this.selections;
18571 this.cmp.removeClass(this.selectedClass);
18572 this.selections = [];
18573 if(!suppressEvent){
18574 this.fireEvent("selectionchange", this, this.selections);
18580 * Returns true if the passed node is selected
18581 * @param {HTMLElement/Number} node The node or node index
18582 * @return {Boolean}
18584 isSelected : function(node){
18585 var s = this.selections;
18589 node = this.getNode(node);
18590 return s.indexOf(node) !== -1;
18595 * @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
18596 * @param {Boolean} keepExisting (optional) true to keep existing selections
18597 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18599 select : function(nodeInfo, keepExisting, suppressEvent){
18600 if(nodeInfo instanceof Array){
18602 this.clearSelections(true);
18604 for(var i = 0, len = nodeInfo.length; i < len; i++){
18605 this.select(nodeInfo[i], true, true);
18609 var node = this.getNode(nodeInfo);
18610 if(!node || this.isSelected(node)){
18611 return; // already selected.
18614 this.clearSelections(true);
18617 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18618 Roo.fly(node).addClass(this.selectedClass);
18619 this.selections.push(node);
18620 if(!suppressEvent){
18621 this.fireEvent("selectionchange", this, this.selections);
18629 * @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
18630 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18631 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18633 unselect : function(nodeInfo, keepExisting, suppressEvent)
18635 if(nodeInfo instanceof Array){
18636 Roo.each(this.selections, function(s) {
18637 this.unselect(s, nodeInfo);
18641 var node = this.getNode(nodeInfo);
18642 if(!node || !this.isSelected(node)){
18643 //Roo.log("not selected");
18644 return; // not selected.
18648 Roo.each(this.selections, function(s) {
18650 Roo.fly(node).removeClass(this.selectedClass);
18657 this.selections= ns;
18658 this.fireEvent("selectionchange", this, this.selections);
18662 * Gets a template node.
18663 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18664 * @return {HTMLElement} The node or null if it wasn't found
18666 getNode : function(nodeInfo){
18667 if(typeof nodeInfo == "string"){
18668 return document.getElementById(nodeInfo);
18669 }else if(typeof nodeInfo == "number"){
18670 return this.nodes[nodeInfo];
18676 * Gets a range template nodes.
18677 * @param {Number} startIndex
18678 * @param {Number} endIndex
18679 * @return {Array} An array of nodes
18681 getNodes : function(start, end){
18682 var ns = this.nodes;
18683 start = start || 0;
18684 end = typeof end == "undefined" ? ns.length - 1 : end;
18687 for(var i = start; i <= end; i++){
18691 for(var i = start; i >= end; i--){
18699 * Finds the index of the passed node
18700 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18701 * @return {Number} The index of the node or -1
18703 indexOf : function(node){
18704 node = this.getNode(node);
18705 if(typeof node.nodeIndex == "number"){
18706 return node.nodeIndex;
18708 var ns = this.nodes;
18709 for(var i = 0, len = ns.length; i < len; i++){
18720 * based on jquery fullcalendar
18724 Roo.bootstrap = Roo.bootstrap || {};
18726 * @class Roo.bootstrap.Calendar
18727 * @extends Roo.bootstrap.Component
18728 * Bootstrap Calendar class
18729 * @cfg {Boolean} loadMask (true|false) default false
18730 * @cfg {Object} header generate the user specific header of the calendar, default false
18733 * Create a new Container
18734 * @param {Object} config The config object
18739 Roo.bootstrap.Calendar = function(config){
18740 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18744 * Fires when a date is selected
18745 * @param {DatePicker} this
18746 * @param {Date} date The selected date
18750 * @event monthchange
18751 * Fires when the displayed month changes
18752 * @param {DatePicker} this
18753 * @param {Date} date The selected month
18755 'monthchange': true,
18757 * @event evententer
18758 * Fires when mouse over an event
18759 * @param {Calendar} this
18760 * @param {event} Event
18762 'evententer': true,
18764 * @event eventleave
18765 * Fires when the mouse leaves an
18766 * @param {Calendar} this
18769 'eventleave': true,
18771 * @event eventclick
18772 * Fires when the mouse click an
18773 * @param {Calendar} this
18782 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18785 * @cfg {Number} startDay
18786 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18794 getAutoCreate : function(){
18797 var fc_button = function(name, corner, style, content ) {
18798 return Roo.apply({},{
18800 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18802 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18805 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18816 style : 'width:100%',
18823 cls : 'fc-header-left',
18825 fc_button('prev', 'left', 'arrow', '‹' ),
18826 fc_button('next', 'right', 'arrow', '›' ),
18827 { tag: 'span', cls: 'fc-header-space' },
18828 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18836 cls : 'fc-header-center',
18840 cls: 'fc-header-title',
18843 html : 'month / year'
18851 cls : 'fc-header-right',
18853 /* fc_button('month', 'left', '', 'month' ),
18854 fc_button('week', '', '', 'week' ),
18855 fc_button('day', 'right', '', 'day' )
18867 header = this.header;
18870 var cal_heads = function() {
18872 // fixme - handle this.
18874 for (var i =0; i < Date.dayNames.length; i++) {
18875 var d = Date.dayNames[i];
18878 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18879 html : d.substring(0,3)
18883 ret[0].cls += ' fc-first';
18884 ret[6].cls += ' fc-last';
18887 var cal_cell = function(n) {
18890 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18895 cls: 'fc-day-number',
18899 cls: 'fc-day-content',
18903 style: 'position: relative;' // height: 17px;
18915 var cal_rows = function() {
18918 for (var r = 0; r < 6; r++) {
18925 for (var i =0; i < Date.dayNames.length; i++) {
18926 var d = Date.dayNames[i];
18927 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18930 row.cn[0].cls+=' fc-first';
18931 row.cn[0].cn[0].style = 'min-height:90px';
18932 row.cn[6].cls+=' fc-last';
18936 ret[0].cls += ' fc-first';
18937 ret[4].cls += ' fc-prev-last';
18938 ret[5].cls += ' fc-last';
18945 cls: 'fc-border-separate',
18946 style : 'width:100%',
18954 cls : 'fc-first fc-last',
18972 cls : 'fc-content',
18973 style : "position: relative;",
18976 cls : 'fc-view fc-view-month fc-grid',
18977 style : 'position: relative',
18978 unselectable : 'on',
18981 cls : 'fc-event-container',
18982 style : 'position:absolute;z-index:8;top:0;left:0;'
19000 initEvents : function()
19003 throw "can not find store for calendar";
19009 style: "text-align:center",
19013 style: "background-color:white;width:50%;margin:250 auto",
19017 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19028 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19030 var size = this.el.select('.fc-content', true).first().getSize();
19031 this.maskEl.setSize(size.width, size.height);
19032 this.maskEl.enableDisplayMode("block");
19033 if(!this.loadMask){
19034 this.maskEl.hide();
19037 this.store = Roo.factory(this.store, Roo.data);
19038 this.store.on('load', this.onLoad, this);
19039 this.store.on('beforeload', this.onBeforeLoad, this);
19043 this.cells = this.el.select('.fc-day',true);
19044 //Roo.log(this.cells);
19045 this.textNodes = this.el.query('.fc-day-number');
19046 this.cells.addClassOnOver('fc-state-hover');
19048 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19049 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19050 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19051 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19053 this.on('monthchange', this.onMonthChange, this);
19055 this.update(new Date().clearTime());
19058 resize : function() {
19059 var sz = this.el.getSize();
19061 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19062 this.el.select('.fc-day-content div',true).setHeight(34);
19067 showPrevMonth : function(e){
19068 this.update(this.activeDate.add("mo", -1));
19070 showToday : function(e){
19071 this.update(new Date().clearTime());
19074 showNextMonth : function(e){
19075 this.update(this.activeDate.add("mo", 1));
19079 showPrevYear : function(){
19080 this.update(this.activeDate.add("y", -1));
19084 showNextYear : function(){
19085 this.update(this.activeDate.add("y", 1));
19090 update : function(date)
19092 var vd = this.activeDate;
19093 this.activeDate = date;
19094 // if(vd && this.el){
19095 // var t = date.getTime();
19096 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19097 // Roo.log('using add remove');
19099 // this.fireEvent('monthchange', this, date);
19101 // this.cells.removeClass("fc-state-highlight");
19102 // this.cells.each(function(c){
19103 // if(c.dateValue == t){
19104 // c.addClass("fc-state-highlight");
19105 // setTimeout(function(){
19106 // try{c.dom.firstChild.focus();}catch(e){}
19116 var days = date.getDaysInMonth();
19118 var firstOfMonth = date.getFirstDateOfMonth();
19119 var startingPos = firstOfMonth.getDay()-this.startDay;
19121 if(startingPos < this.startDay){
19125 var pm = date.add(Date.MONTH, -1);
19126 var prevStart = pm.getDaysInMonth()-startingPos;
19128 this.cells = this.el.select('.fc-day',true);
19129 this.textNodes = this.el.query('.fc-day-number');
19130 this.cells.addClassOnOver('fc-state-hover');
19132 var cells = this.cells.elements;
19133 var textEls = this.textNodes;
19135 Roo.each(cells, function(cell){
19136 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19139 days += startingPos;
19141 // convert everything to numbers so it's fast
19142 var day = 86400000;
19143 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19146 //Roo.log(prevStart);
19148 var today = new Date().clearTime().getTime();
19149 var sel = date.clearTime().getTime();
19150 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19151 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19152 var ddMatch = this.disabledDatesRE;
19153 var ddText = this.disabledDatesText;
19154 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19155 var ddaysText = this.disabledDaysText;
19156 var format = this.format;
19158 var setCellClass = function(cal, cell){
19162 //Roo.log('set Cell Class');
19164 var t = d.getTime();
19168 cell.dateValue = t;
19170 cell.className += " fc-today";
19171 cell.className += " fc-state-highlight";
19172 cell.title = cal.todayText;
19175 // disable highlight in other month..
19176 //cell.className += " fc-state-highlight";
19181 cell.className = " fc-state-disabled";
19182 cell.title = cal.minText;
19186 cell.className = " fc-state-disabled";
19187 cell.title = cal.maxText;
19191 if(ddays.indexOf(d.getDay()) != -1){
19192 cell.title = ddaysText;
19193 cell.className = " fc-state-disabled";
19196 if(ddMatch && format){
19197 var fvalue = d.dateFormat(format);
19198 if(ddMatch.test(fvalue)){
19199 cell.title = ddText.replace("%0", fvalue);
19200 cell.className = " fc-state-disabled";
19204 if (!cell.initialClassName) {
19205 cell.initialClassName = cell.dom.className;
19208 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19213 for(; i < startingPos; i++) {
19214 textEls[i].innerHTML = (++prevStart);
19215 d.setDate(d.getDate()+1);
19217 cells[i].className = "fc-past fc-other-month";
19218 setCellClass(this, cells[i]);
19223 for(; i < days; i++){
19224 intDay = i - startingPos + 1;
19225 textEls[i].innerHTML = (intDay);
19226 d.setDate(d.getDate()+1);
19228 cells[i].className = ''; // "x-date-active";
19229 setCellClass(this, cells[i]);
19233 for(; i < 42; i++) {
19234 textEls[i].innerHTML = (++extraDays);
19235 d.setDate(d.getDate()+1);
19237 cells[i].className = "fc-future fc-other-month";
19238 setCellClass(this, cells[i]);
19241 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19243 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19245 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19246 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19248 if(totalRows != 6){
19249 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19250 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19253 this.fireEvent('monthchange', this, date);
19257 if(!this.internalRender){
19258 var main = this.el.dom.firstChild;
19259 var w = main.offsetWidth;
19260 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19261 Roo.fly(main).setWidth(w);
19262 this.internalRender = true;
19263 // opera does not respect the auto grow header center column
19264 // then, after it gets a width opera refuses to recalculate
19265 // without a second pass
19266 if(Roo.isOpera && !this.secondPass){
19267 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19268 this.secondPass = true;
19269 this.update.defer(10, this, [date]);
19276 findCell : function(dt) {
19277 dt = dt.clearTime().getTime();
19279 this.cells.each(function(c){
19280 //Roo.log("check " +c.dateValue + '?=' + dt);
19281 if(c.dateValue == dt){
19291 findCells : function(ev) {
19292 var s = ev.start.clone().clearTime().getTime();
19294 var e= ev.end.clone().clearTime().getTime();
19297 this.cells.each(function(c){
19298 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19300 if(c.dateValue > e){
19303 if(c.dateValue < s){
19312 // findBestRow: function(cells)
19316 // for (var i =0 ; i < cells.length;i++) {
19317 // ret = Math.max(cells[i].rows || 0,ret);
19324 addItem : function(ev)
19326 // look for vertical location slot in
19327 var cells = this.findCells(ev);
19329 // ev.row = this.findBestRow(cells);
19331 // work out the location.
19335 for(var i =0; i < cells.length; i++) {
19337 cells[i].row = cells[0].row;
19340 cells[i].row = cells[i].row + 1;
19350 if (crow.start.getY() == cells[i].getY()) {
19352 crow.end = cells[i];
19369 cells[0].events.push(ev);
19371 this.calevents.push(ev);
19374 clearEvents: function() {
19376 if(!this.calevents){
19380 Roo.each(this.cells.elements, function(c){
19386 Roo.each(this.calevents, function(e) {
19387 Roo.each(e.els, function(el) {
19388 el.un('mouseenter' ,this.onEventEnter, this);
19389 el.un('mouseleave' ,this.onEventLeave, this);
19394 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19400 renderEvents: function()
19404 this.cells.each(function(c) {
19413 if(c.row != c.events.length){
19414 r = 4 - (4 - (c.row - c.events.length));
19417 c.events = ev.slice(0, r);
19418 c.more = ev.slice(r);
19420 if(c.more.length && c.more.length == 1){
19421 c.events.push(c.more.pop());
19424 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19428 this.cells.each(function(c) {
19430 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19433 for (var e = 0; e < c.events.length; e++){
19434 var ev = c.events[e];
19435 var rows = ev.rows;
19437 for(var i = 0; i < rows.length; i++) {
19439 // how many rows should it span..
19442 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19443 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19445 unselectable : "on",
19448 cls: 'fc-event-inner',
19452 // cls: 'fc-event-time',
19453 // html : cells.length > 1 ? '' : ev.time
19457 cls: 'fc-event-title',
19458 html : String.format('{0}', ev.title)
19465 cls: 'ui-resizable-handle ui-resizable-e',
19466 html : '  '
19473 cfg.cls += ' fc-event-start';
19475 if ((i+1) == rows.length) {
19476 cfg.cls += ' fc-event-end';
19479 var ctr = _this.el.select('.fc-event-container',true).first();
19480 var cg = ctr.createChild(cfg);
19482 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19483 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19485 var r = (c.more.length) ? 1 : 0;
19486 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19487 cg.setWidth(ebox.right - sbox.x -2);
19489 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19490 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19491 cg.on('click', _this.onEventClick, _this, ev);
19502 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19503 style : 'position: absolute',
19504 unselectable : "on",
19507 cls: 'fc-event-inner',
19511 cls: 'fc-event-title',
19519 cls: 'ui-resizable-handle ui-resizable-e',
19520 html : '  '
19526 var ctr = _this.el.select('.fc-event-container',true).first();
19527 var cg = ctr.createChild(cfg);
19529 var sbox = c.select('.fc-day-content',true).first().getBox();
19530 var ebox = c.select('.fc-day-content',true).first().getBox();
19532 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19533 cg.setWidth(ebox.right - sbox.x -2);
19535 cg.on('click', _this.onMoreEventClick, _this, c.more);
19545 onEventEnter: function (e, el,event,d) {
19546 this.fireEvent('evententer', this, el, event);
19549 onEventLeave: function (e, el,event,d) {
19550 this.fireEvent('eventleave', this, el, event);
19553 onEventClick: function (e, el,event,d) {
19554 this.fireEvent('eventclick', this, el, event);
19557 onMonthChange: function () {
19561 onMoreEventClick: function(e, el, more)
19565 this.calpopover.placement = 'right';
19566 this.calpopover.setTitle('More');
19568 this.calpopover.setContent('');
19570 var ctr = this.calpopover.el.select('.popover-content', true).first();
19572 Roo.each(more, function(m){
19574 cls : 'fc-event-hori fc-event-draggable',
19577 var cg = ctr.createChild(cfg);
19579 cg.on('click', _this.onEventClick, _this, m);
19582 this.calpopover.show(el);
19587 onLoad: function ()
19589 this.calevents = [];
19592 if(this.store.getCount() > 0){
19593 this.store.data.each(function(d){
19596 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19597 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19598 time : d.data.start_time,
19599 title : d.data.title,
19600 description : d.data.description,
19601 venue : d.data.venue
19606 this.renderEvents();
19608 if(this.calevents.length && this.loadMask){
19609 this.maskEl.hide();
19613 onBeforeLoad: function()
19615 this.clearEvents();
19617 this.maskEl.show();
19631 * @class Roo.bootstrap.Popover
19632 * @extends Roo.bootstrap.Component
19633 * Bootstrap Popover class
19634 * @cfg {String} html contents of the popover (or false to use children..)
19635 * @cfg {String} title of popover (or false to hide)
19636 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19637 * @cfg {String} trigger click || hover (or false to trigger manually)
19638 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19639 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19640 * - if false and it has a 'parent' then it will be automatically added to that element
19641 * - if string - Roo.get will be called
19642 * @cfg {Number} delay - delay before showing
19645 * Create a new Popover
19646 * @param {Object} config The config object
19649 Roo.bootstrap.Popover = function(config){
19650 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19656 * After the popover show
19658 * @param {Roo.bootstrap.Popover} this
19663 * After the popover hide
19665 * @param {Roo.bootstrap.Popover} this
19671 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19676 placement : 'right',
19677 trigger : 'hover', // hover
19683 can_build_overlaid : false,
19685 maskEl : false, // the mask element
19688 alignEl : false, // when show is called with an element - this get's stored.
19690 getChildContainer : function()
19692 return this.contentEl;
19695 getPopoverHeader : function()
19697 this.title = true; // flag not to hide it..
19698 this.headerEl.addClass('p-0');
19699 return this.headerEl
19703 getAutoCreate : function(){
19706 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19707 style: 'display:block',
19713 cls : 'popover-inner ',
19717 cls: 'popover-title popover-header',
19718 html : this.title === false ? '' : this.title
19721 cls : 'popover-content popover-body ' + (this.cls || ''),
19722 html : this.html || ''
19733 * @param {string} the title
19735 setTitle: function(str)
19739 this.headerEl.dom.innerHTML = str;
19744 * @param {string} the body content
19746 setContent: function(str)
19749 if (this.contentEl) {
19750 this.contentEl.dom.innerHTML = str;
19754 // as it get's added to the bottom of the page.
19755 onRender : function(ct, position)
19757 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19762 var cfg = Roo.apply({}, this.getAutoCreate());
19766 cfg.cls += ' ' + this.cls;
19769 cfg.style = this.style;
19771 //Roo.log("adding to ");
19772 this.el = Roo.get(document.body).createChild(cfg, position);
19773 // Roo.log(this.el);
19776 this.contentEl = this.el.select('.popover-content',true).first();
19777 this.headerEl = this.el.select('.popover-title',true).first();
19780 if(typeof(this.items) != 'undefined'){
19781 var items = this.items;
19784 for(var i =0;i < items.length;i++) {
19785 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19789 this.items = nitems;
19791 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19792 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19799 resizeMask : function()
19801 this.maskEl.setSize(
19802 Roo.lib.Dom.getViewWidth(true),
19803 Roo.lib.Dom.getViewHeight(true)
19807 initEvents : function()
19811 Roo.bootstrap.Popover.register(this);
19814 this.arrowEl = this.el.select('.arrow',true).first();
19815 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19816 this.el.enableDisplayMode('block');
19820 if (this.over === false && !this.parent()) {
19823 if (this.triggers === false) {
19828 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19829 var triggers = this.trigger ? this.trigger.split(' ') : [];
19830 Roo.each(triggers, function(trigger) {
19832 if (trigger == 'click') {
19833 on_el.on('click', this.toggle, this);
19834 } else if (trigger != 'manual') {
19835 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19836 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19838 on_el.on(eventIn ,this.enter, this);
19839 on_el.on(eventOut, this.leave, this);
19849 toggle : function () {
19850 this.hoverState == 'in' ? this.leave() : this.enter();
19853 enter : function () {
19855 clearTimeout(this.timeout);
19857 this.hoverState = 'in';
19859 if (!this.delay || !this.delay.show) {
19864 this.timeout = setTimeout(function () {
19865 if (_t.hoverState == 'in') {
19868 }, this.delay.show)
19871 leave : function() {
19872 clearTimeout(this.timeout);
19874 this.hoverState = 'out';
19876 if (!this.delay || !this.delay.hide) {
19881 this.timeout = setTimeout(function () {
19882 if (_t.hoverState == 'out') {
19885 }, this.delay.hide)
19889 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19890 * @param {string} (left|right|top|bottom) position
19892 show : function (on_el, placement)
19894 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19895 on_el = on_el || false; // default to false
19898 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19899 on_el = this.parent().el;
19900 } else if (this.over) {
19901 Roo.get(this.over);
19906 this.alignEl = Roo.get( on_el );
19909 this.render(document.body);
19915 if (this.title === false) {
19916 this.headerEl.hide();
19921 this.el.dom.style.display = 'block';
19924 if (this.alignEl) {
19925 this.updatePosition(this.placement, true);
19928 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19929 var es = this.el.getSize();
19930 var x = Roo.lib.Dom.getViewWidth()/2;
19931 var y = Roo.lib.Dom.getViewHeight()/2;
19932 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19937 //var arrow = this.el.select('.arrow',true).first();
19938 //arrow.set(align[2],
19940 this.el.addClass('in');
19944 this.hoverState = 'in';
19947 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19948 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19949 this.maskEl.dom.style.display = 'block';
19950 this.maskEl.addClass('show');
19952 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19954 this.fireEvent('show', this);
19958 * fire this manually after loading a grid in the table for example
19959 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19960 * @param {Boolean} try and move it if we cant get right position.
19962 updatePosition : function(placement, try_move)
19964 // allow for calling with no parameters
19965 placement = placement ? placement : this.placement;
19966 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19968 this.el.removeClass([
19969 'fade','top','bottom', 'left', 'right','in',
19970 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19972 this.el.addClass(placement + ' bs-popover-' + placement);
19974 if (!this.alignEl ) {
19978 switch (placement) {
19980 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19981 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19982 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19983 //normal display... or moved up/down.
19984 this.el.setXY(offset);
19985 var xy = this.alignEl.getAnchorXY('tr', false);
19987 this.arrowEl.setXY(xy);
19990 // continue through...
19991 return this.updatePosition('left', false);
19995 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19996 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19997 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19998 //normal display... or moved up/down.
19999 this.el.setXY(offset);
20000 var xy = this.alignEl.getAnchorXY('tl', false);
20001 xy[0]-=10;xy[1]+=5; // << fix me
20002 this.arrowEl.setXY(xy);
20006 return this.updatePosition('right', false);
20009 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20010 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20011 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20012 //normal display... or moved up/down.
20013 this.el.setXY(offset);
20014 var xy = this.alignEl.getAnchorXY('t', false);
20015 xy[1]-=10; // << fix me
20016 this.arrowEl.setXY(xy);
20020 return this.updatePosition('bottom', false);
20023 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20024 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20025 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20026 //normal display... or moved up/down.
20027 this.el.setXY(offset);
20028 var xy = this.alignEl.getAnchorXY('b', false);
20029 xy[1]+=2; // << fix me
20030 this.arrowEl.setXY(xy);
20034 return this.updatePosition('top', false);
20045 this.el.setXY([0,0]);
20046 this.el.removeClass('in');
20048 this.hoverState = null;
20049 this.maskEl.hide(); // always..
20050 this.fireEvent('hide', this);
20056 Roo.apply(Roo.bootstrap.Popover, {
20059 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20060 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20061 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20062 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20067 clickHander : false,
20070 onMouseDown : function(e)
20072 if (!e.getTarget(".roo-popover")) {
20080 register : function(popup)
20082 if (!Roo.bootstrap.Popover.clickHandler) {
20083 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20085 // hide other popups.
20087 this.popups.push(popup);
20089 hideAll : function()
20091 this.popups.forEach(function(p) {
20099 * Card header - holder for the card header elements.
20104 * @class Roo.bootstrap.PopoverNav
20105 * @extends Roo.bootstrap.NavGroup
20106 * Bootstrap Popover header navigation class
20108 * Create a new Popover Header Navigation
20109 * @param {Object} config The config object
20112 Roo.bootstrap.PopoverNav = function(config){
20113 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20116 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20119 container_method : 'getPopoverHeader'
20137 * @class Roo.bootstrap.Progress
20138 * @extends Roo.bootstrap.Component
20139 * Bootstrap Progress class
20140 * @cfg {Boolean} striped striped of the progress bar
20141 * @cfg {Boolean} active animated of the progress bar
20145 * Create a new Progress
20146 * @param {Object} config The config object
20149 Roo.bootstrap.Progress = function(config){
20150 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20153 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20158 getAutoCreate : function(){
20166 cfg.cls += ' progress-striped';
20170 cfg.cls += ' active';
20189 * @class Roo.bootstrap.ProgressBar
20190 * @extends Roo.bootstrap.Component
20191 * Bootstrap ProgressBar class
20192 * @cfg {Number} aria_valuenow aria-value now
20193 * @cfg {Number} aria_valuemin aria-value min
20194 * @cfg {Number} aria_valuemax aria-value max
20195 * @cfg {String} label label for the progress bar
20196 * @cfg {String} panel (success | info | warning | danger )
20197 * @cfg {String} role role of the progress bar
20198 * @cfg {String} sr_only text
20202 * Create a new ProgressBar
20203 * @param {Object} config The config object
20206 Roo.bootstrap.ProgressBar = function(config){
20207 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20210 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20214 aria_valuemax : 100,
20220 getAutoCreate : function()
20225 cls: 'progress-bar',
20226 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20238 cfg.role = this.role;
20241 if(this.aria_valuenow){
20242 cfg['aria-valuenow'] = this.aria_valuenow;
20245 if(this.aria_valuemin){
20246 cfg['aria-valuemin'] = this.aria_valuemin;
20249 if(this.aria_valuemax){
20250 cfg['aria-valuemax'] = this.aria_valuemax;
20253 if(this.label && !this.sr_only){
20254 cfg.html = this.label;
20258 cfg.cls += ' progress-bar-' + this.panel;
20264 update : function(aria_valuenow)
20266 this.aria_valuenow = aria_valuenow;
20268 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20283 * @class Roo.bootstrap.TabGroup
20284 * @extends Roo.bootstrap.Column
20285 * Bootstrap Column class
20286 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20287 * @cfg {Boolean} carousel true to make the group behave like a carousel
20288 * @cfg {Boolean} bullets show bullets for the panels
20289 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20290 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20291 * @cfg {Boolean} showarrow (true|false) show arrow default true
20294 * Create a new TabGroup
20295 * @param {Object} config The config object
20298 Roo.bootstrap.TabGroup = function(config){
20299 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20301 this.navId = Roo.id();
20304 Roo.bootstrap.TabGroup.register(this);
20308 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20311 transition : false,
20316 slideOnTouch : false,
20319 getAutoCreate : function()
20321 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20323 cfg.cls += ' tab-content';
20325 if (this.carousel) {
20326 cfg.cls += ' carousel slide';
20329 cls : 'carousel-inner',
20333 if(this.bullets && !Roo.isTouch){
20336 cls : 'carousel-bullets',
20340 if(this.bullets_cls){
20341 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20348 cfg.cn[0].cn.push(bullets);
20351 if(this.showarrow){
20352 cfg.cn[0].cn.push({
20354 class : 'carousel-arrow',
20358 class : 'carousel-prev',
20362 class : 'fa fa-chevron-left'
20368 class : 'carousel-next',
20372 class : 'fa fa-chevron-right'
20385 initEvents: function()
20387 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20388 // this.el.on("touchstart", this.onTouchStart, this);
20391 if(this.autoslide){
20394 this.slideFn = window.setInterval(function() {
20395 _this.showPanelNext();
20399 if(this.showarrow){
20400 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20401 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20407 // onTouchStart : function(e, el, o)
20409 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20413 // this.showPanelNext();
20417 getChildContainer : function()
20419 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20423 * register a Navigation item
20424 * @param {Roo.bootstrap.NavItem} the navitem to add
20426 register : function(item)
20428 this.tabs.push( item);
20429 item.navId = this.navId; // not really needed..
20434 getActivePanel : function()
20437 Roo.each(this.tabs, function(t) {
20447 getPanelByName : function(n)
20450 Roo.each(this.tabs, function(t) {
20451 if (t.tabId == n) {
20459 indexOfPanel : function(p)
20462 Roo.each(this.tabs, function(t,i) {
20463 if (t.tabId == p.tabId) {
20472 * show a specific panel
20473 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20474 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20476 showPanel : function (pan)
20478 if(this.transition || typeof(pan) == 'undefined'){
20479 Roo.log("waiting for the transitionend");
20483 if (typeof(pan) == 'number') {
20484 pan = this.tabs[pan];
20487 if (typeof(pan) == 'string') {
20488 pan = this.getPanelByName(pan);
20491 var cur = this.getActivePanel();
20494 Roo.log('pan or acitve pan is undefined');
20498 if (pan.tabId == this.getActivePanel().tabId) {
20502 if (false === cur.fireEvent('beforedeactivate')) {
20506 if(this.bullets > 0 && !Roo.isTouch){
20507 this.setActiveBullet(this.indexOfPanel(pan));
20510 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20512 //class="carousel-item carousel-item-next carousel-item-left"
20514 this.transition = true;
20515 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20516 var lr = dir == 'next' ? 'left' : 'right';
20517 pan.el.addClass(dir); // or prev
20518 pan.el.addClass('carousel-item-' + dir); // or prev
20519 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20520 cur.el.addClass(lr); // or right
20521 pan.el.addClass(lr);
20522 cur.el.addClass('carousel-item-' +lr); // or right
20523 pan.el.addClass('carousel-item-' +lr);
20527 cur.el.on('transitionend', function() {
20528 Roo.log("trans end?");
20530 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20531 pan.setActive(true);
20533 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20534 cur.setActive(false);
20536 _this.transition = false;
20538 }, this, { single: true } );
20543 cur.setActive(false);
20544 pan.setActive(true);
20549 showPanelNext : function()
20551 var i = this.indexOfPanel(this.getActivePanel());
20553 if (i >= this.tabs.length - 1 && !this.autoslide) {
20557 if (i >= this.tabs.length - 1 && this.autoslide) {
20561 this.showPanel(this.tabs[i+1]);
20564 showPanelPrev : function()
20566 var i = this.indexOfPanel(this.getActivePanel());
20568 if (i < 1 && !this.autoslide) {
20572 if (i < 1 && this.autoslide) {
20573 i = this.tabs.length;
20576 this.showPanel(this.tabs[i-1]);
20580 addBullet: function()
20582 if(!this.bullets || Roo.isTouch){
20585 var ctr = this.el.select('.carousel-bullets',true).first();
20586 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20587 var bullet = ctr.createChild({
20588 cls : 'bullet bullet-' + i
20589 },ctr.dom.lastChild);
20594 bullet.on('click', (function(e, el, o, ii, t){
20596 e.preventDefault();
20598 this.showPanel(ii);
20600 if(this.autoslide && this.slideFn){
20601 clearInterval(this.slideFn);
20602 this.slideFn = window.setInterval(function() {
20603 _this.showPanelNext();
20607 }).createDelegate(this, [i, bullet], true));
20612 setActiveBullet : function(i)
20618 Roo.each(this.el.select('.bullet', true).elements, function(el){
20619 el.removeClass('selected');
20622 var bullet = this.el.select('.bullet-' + i, true).first();
20628 bullet.addClass('selected');
20639 Roo.apply(Roo.bootstrap.TabGroup, {
20643 * register a Navigation Group
20644 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20646 register : function(navgrp)
20648 this.groups[navgrp.navId] = navgrp;
20652 * fetch a Navigation Group based on the navigation ID
20653 * if one does not exist , it will get created.
20654 * @param {string} the navgroup to add
20655 * @returns {Roo.bootstrap.NavGroup} the navgroup
20657 get: function(navId) {
20658 if (typeof(this.groups[navId]) == 'undefined') {
20659 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20661 return this.groups[navId] ;
20676 * @class Roo.bootstrap.TabPanel
20677 * @extends Roo.bootstrap.Component
20678 * Bootstrap TabPanel class
20679 * @cfg {Boolean} active panel active
20680 * @cfg {String} html panel content
20681 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20682 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20683 * @cfg {String} href click to link..
20684 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20688 * Create a new TabPanel
20689 * @param {Object} config The config object
20692 Roo.bootstrap.TabPanel = function(config){
20693 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20697 * Fires when the active status changes
20698 * @param {Roo.bootstrap.TabPanel} this
20699 * @param {Boolean} state the new state
20704 * @event beforedeactivate
20705 * Fires before a tab is de-activated - can be used to do validation on a form.
20706 * @param {Roo.bootstrap.TabPanel} this
20707 * @return {Boolean} false if there is an error
20710 'beforedeactivate': true
20713 this.tabId = this.tabId || Roo.id();
20717 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20724 touchSlide : false,
20725 getAutoCreate : function(){
20730 // item is needed for carousel - not sure if it has any effect otherwise
20731 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20732 html: this.html || ''
20736 cfg.cls += ' active';
20740 cfg.tabId = this.tabId;
20748 initEvents: function()
20750 var p = this.parent();
20752 this.navId = this.navId || p.navId;
20754 if (typeof(this.navId) != 'undefined') {
20755 // not really needed.. but just in case.. parent should be a NavGroup.
20756 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20760 var i = tg.tabs.length - 1;
20762 if(this.active && tg.bullets > 0 && i < tg.bullets){
20763 tg.setActiveBullet(i);
20767 this.el.on('click', this.onClick, this);
20769 if(Roo.isTouch && this.touchSlide){
20770 this.el.on("touchstart", this.onTouchStart, this);
20771 this.el.on("touchmove", this.onTouchMove, this);
20772 this.el.on("touchend", this.onTouchEnd, this);
20777 onRender : function(ct, position)
20779 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20782 setActive : function(state)
20784 Roo.log("panel - set active " + this.tabId + "=" + state);
20786 this.active = state;
20788 this.el.removeClass('active');
20790 } else if (!this.el.hasClass('active')) {
20791 this.el.addClass('active');
20794 this.fireEvent('changed', this, state);
20797 onClick : function(e)
20799 e.preventDefault();
20801 if(!this.href.length){
20805 window.location.href = this.href;
20814 onTouchStart : function(e)
20816 this.swiping = false;
20818 this.startX = e.browserEvent.touches[0].clientX;
20819 this.startY = e.browserEvent.touches[0].clientY;
20822 onTouchMove : function(e)
20824 this.swiping = true;
20826 this.endX = e.browserEvent.touches[0].clientX;
20827 this.endY = e.browserEvent.touches[0].clientY;
20830 onTouchEnd : function(e)
20837 var tabGroup = this.parent();
20839 if(this.endX > this.startX){ // swiping right
20840 tabGroup.showPanelPrev();
20844 if(this.startX > this.endX){ // swiping left
20845 tabGroup.showPanelNext();
20864 * @class Roo.bootstrap.DateField
20865 * @extends Roo.bootstrap.Input
20866 * Bootstrap DateField class
20867 * @cfg {Number} weekStart default 0
20868 * @cfg {String} viewMode default empty, (months|years)
20869 * @cfg {String} minViewMode default empty, (months|years)
20870 * @cfg {Number} startDate default -Infinity
20871 * @cfg {Number} endDate default Infinity
20872 * @cfg {Boolean} todayHighlight default false
20873 * @cfg {Boolean} todayBtn default false
20874 * @cfg {Boolean} calendarWeeks default false
20875 * @cfg {Object} daysOfWeekDisabled default empty
20876 * @cfg {Boolean} singleMode default false (true | false)
20878 * @cfg {Boolean} keyboardNavigation default true
20879 * @cfg {String} language default en
20882 * Create a new DateField
20883 * @param {Object} config The config object
20886 Roo.bootstrap.DateField = function(config){
20887 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20891 * Fires when this field show.
20892 * @param {Roo.bootstrap.DateField} this
20893 * @param {Mixed} date The date value
20898 * Fires when this field hide.
20899 * @param {Roo.bootstrap.DateField} this
20900 * @param {Mixed} date The date value
20905 * Fires when select a date.
20906 * @param {Roo.bootstrap.DateField} this
20907 * @param {Mixed} date The date value
20911 * @event beforeselect
20912 * Fires when before select a date.
20913 * @param {Roo.bootstrap.DateField} this
20914 * @param {Mixed} date The date value
20916 beforeselect : true
20920 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20923 * @cfg {String} format
20924 * The default date format string which can be overriden for localization support. The format must be
20925 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20929 * @cfg {String} altFormats
20930 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20931 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20933 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20941 todayHighlight : false,
20947 keyboardNavigation: true,
20949 calendarWeeks: false,
20951 startDate: -Infinity,
20955 daysOfWeekDisabled: [],
20959 singleMode : false,
20961 UTCDate: function()
20963 return new Date(Date.UTC.apply(Date, arguments));
20966 UTCToday: function()
20968 var today = new Date();
20969 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20972 getDate: function() {
20973 var d = this.getUTCDate();
20974 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20977 getUTCDate: function() {
20981 setDate: function(d) {
20982 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20985 setUTCDate: function(d) {
20987 this.setValue(this.formatDate(this.date));
20990 onRender: function(ct, position)
20993 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20995 this.language = this.language || 'en';
20996 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20997 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20999 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21000 this.format = this.format || 'm/d/y';
21001 this.isInline = false;
21002 this.isInput = true;
21003 this.component = this.el.select('.add-on', true).first() || false;
21004 this.component = (this.component && this.component.length === 0) ? false : this.component;
21005 this.hasInput = this.component && this.inputEl().length;
21007 if (typeof(this.minViewMode === 'string')) {
21008 switch (this.minViewMode) {
21010 this.minViewMode = 1;
21013 this.minViewMode = 2;
21016 this.minViewMode = 0;
21021 if (typeof(this.viewMode === 'string')) {
21022 switch (this.viewMode) {
21035 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21037 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21039 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21041 this.picker().on('mousedown', this.onMousedown, this);
21042 this.picker().on('click', this.onClick, this);
21044 this.picker().addClass('datepicker-dropdown');
21046 this.startViewMode = this.viewMode;
21048 if(this.singleMode){
21049 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21050 v.setVisibilityMode(Roo.Element.DISPLAY);
21054 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21055 v.setStyle('width', '189px');
21059 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21060 if(!this.calendarWeeks){
21065 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21066 v.attr('colspan', function(i, val){
21067 return parseInt(val) + 1;
21072 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21074 this.setStartDate(this.startDate);
21075 this.setEndDate(this.endDate);
21077 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21084 if(this.isInline) {
21089 picker : function()
21091 return this.pickerEl;
21092 // return this.el.select('.datepicker', true).first();
21095 fillDow: function()
21097 var dowCnt = this.weekStart;
21106 if(this.calendarWeeks){
21114 while (dowCnt < this.weekStart + 7) {
21118 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21122 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21125 fillMonths: function()
21128 var months = this.picker().select('>.datepicker-months td', true).first();
21130 months.dom.innerHTML = '';
21136 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21139 months.createChild(month);
21146 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;
21148 if (this.date < this.startDate) {
21149 this.viewDate = new Date(this.startDate);
21150 } else if (this.date > this.endDate) {
21151 this.viewDate = new Date(this.endDate);
21153 this.viewDate = new Date(this.date);
21161 var d = new Date(this.viewDate),
21162 year = d.getUTCFullYear(),
21163 month = d.getUTCMonth(),
21164 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21165 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21166 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21167 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21168 currentDate = this.date && this.date.valueOf(),
21169 today = this.UTCToday();
21171 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21173 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21175 // this.picker.select('>tfoot th.today').
21176 // .text(dates[this.language].today)
21177 // .toggle(this.todayBtn !== false);
21179 this.updateNavArrows();
21182 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21184 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21186 prevMonth.setUTCDate(day);
21188 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21190 var nextMonth = new Date(prevMonth);
21192 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21194 nextMonth = nextMonth.valueOf();
21196 var fillMonths = false;
21198 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21200 while(prevMonth.valueOf() <= nextMonth) {
21203 if (prevMonth.getUTCDay() === this.weekStart) {
21205 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21213 if(this.calendarWeeks){
21214 // ISO 8601: First week contains first thursday.
21215 // ISO also states week starts on Monday, but we can be more abstract here.
21217 // Start of current week: based on weekstart/current date
21218 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21219 // Thursday of this week
21220 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21221 // First Thursday of year, year from thursday
21222 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21223 // Calendar week: ms between thursdays, div ms per day, div 7 days
21224 calWeek = (th - yth) / 864e5 / 7 + 1;
21226 fillMonths.cn.push({
21234 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21236 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21239 if (this.todayHighlight &&
21240 prevMonth.getUTCFullYear() == today.getFullYear() &&
21241 prevMonth.getUTCMonth() == today.getMonth() &&
21242 prevMonth.getUTCDate() == today.getDate()) {
21243 clsName += ' today';
21246 if (currentDate && prevMonth.valueOf() === currentDate) {
21247 clsName += ' active';
21250 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21251 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21252 clsName += ' disabled';
21255 fillMonths.cn.push({
21257 cls: 'day ' + clsName,
21258 html: prevMonth.getDate()
21261 prevMonth.setDate(prevMonth.getDate()+1);
21264 var currentYear = this.date && this.date.getUTCFullYear();
21265 var currentMonth = this.date && this.date.getUTCMonth();
21267 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21269 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21270 v.removeClass('active');
21272 if(currentYear === year && k === currentMonth){
21273 v.addClass('active');
21276 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21277 v.addClass('disabled');
21283 year = parseInt(year/10, 10) * 10;
21285 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21287 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21290 for (var i = -1; i < 11; i++) {
21291 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21293 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21301 showMode: function(dir)
21304 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21307 Roo.each(this.picker().select('>div',true).elements, function(v){
21308 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21311 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21316 if(this.isInline) {
21320 this.picker().removeClass(['bottom', 'top']);
21322 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21324 * place to the top of element!
21328 this.picker().addClass('top');
21329 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21334 this.picker().addClass('bottom');
21336 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21339 parseDate : function(value)
21341 if(!value || value instanceof Date){
21344 var v = Date.parseDate(value, this.format);
21345 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21346 v = Date.parseDate(value, 'Y-m-d');
21348 if(!v && this.altFormats){
21349 if(!this.altFormatsArray){
21350 this.altFormatsArray = this.altFormats.split("|");
21352 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21353 v = Date.parseDate(value, this.altFormatsArray[i]);
21359 formatDate : function(date, fmt)
21361 return (!date || !(date instanceof Date)) ?
21362 date : date.dateFormat(fmt || this.format);
21365 onFocus : function()
21367 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21371 onBlur : function()
21373 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21375 var d = this.inputEl().getValue();
21382 showPopup : function()
21384 this.picker().show();
21388 this.fireEvent('showpopup', this, this.date);
21391 hidePopup : function()
21393 if(this.isInline) {
21396 this.picker().hide();
21397 this.viewMode = this.startViewMode;
21400 this.fireEvent('hidepopup', this, this.date);
21404 onMousedown: function(e)
21406 e.stopPropagation();
21407 e.preventDefault();
21412 Roo.bootstrap.DateField.superclass.keyup.call(this);
21416 setValue: function(v)
21418 if(this.fireEvent('beforeselect', this, v) !== false){
21419 var d = new Date(this.parseDate(v) ).clearTime();
21421 if(isNaN(d.getTime())){
21422 this.date = this.viewDate = '';
21423 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21427 v = this.formatDate(d);
21429 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21431 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21435 this.fireEvent('select', this, this.date);
21439 getValue: function()
21441 return this.formatDate(this.date);
21444 fireKey: function(e)
21446 if (!this.picker().isVisible()){
21447 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21453 var dateChanged = false,
21455 newDate, newViewDate;
21460 e.preventDefault();
21464 if (!this.keyboardNavigation) {
21467 dir = e.keyCode == 37 ? -1 : 1;
21470 newDate = this.moveYear(this.date, dir);
21471 newViewDate = this.moveYear(this.viewDate, dir);
21472 } else if (e.shiftKey){
21473 newDate = this.moveMonth(this.date, dir);
21474 newViewDate = this.moveMonth(this.viewDate, dir);
21476 newDate = new Date(this.date);
21477 newDate.setUTCDate(this.date.getUTCDate() + dir);
21478 newViewDate = new Date(this.viewDate);
21479 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21481 if (this.dateWithinRange(newDate)){
21482 this.date = newDate;
21483 this.viewDate = newViewDate;
21484 this.setValue(this.formatDate(this.date));
21486 e.preventDefault();
21487 dateChanged = true;
21492 if (!this.keyboardNavigation) {
21495 dir = e.keyCode == 38 ? -1 : 1;
21497 newDate = this.moveYear(this.date, dir);
21498 newViewDate = this.moveYear(this.viewDate, dir);
21499 } else if (e.shiftKey){
21500 newDate = this.moveMonth(this.date, dir);
21501 newViewDate = this.moveMonth(this.viewDate, dir);
21503 newDate = new Date(this.date);
21504 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21505 newViewDate = new Date(this.viewDate);
21506 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21508 if (this.dateWithinRange(newDate)){
21509 this.date = newDate;
21510 this.viewDate = newViewDate;
21511 this.setValue(this.formatDate(this.date));
21513 e.preventDefault();
21514 dateChanged = true;
21518 this.setValue(this.formatDate(this.date));
21520 e.preventDefault();
21523 this.setValue(this.formatDate(this.date));
21537 onClick: function(e)
21539 e.stopPropagation();
21540 e.preventDefault();
21542 var target = e.getTarget();
21544 if(target.nodeName.toLowerCase() === 'i'){
21545 target = Roo.get(target).dom.parentNode;
21548 var nodeName = target.nodeName;
21549 var className = target.className;
21550 var html = target.innerHTML;
21551 //Roo.log(nodeName);
21553 switch(nodeName.toLowerCase()) {
21555 switch(className) {
21561 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21562 switch(this.viewMode){
21564 this.viewDate = this.moveMonth(this.viewDate, dir);
21568 this.viewDate = this.moveYear(this.viewDate, dir);
21574 var date = new Date();
21575 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21577 this.setValue(this.formatDate(this.date));
21584 if (className.indexOf('disabled') < 0) {
21585 this.viewDate.setUTCDate(1);
21586 if (className.indexOf('month') > -1) {
21587 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21589 var year = parseInt(html, 10) || 0;
21590 this.viewDate.setUTCFullYear(year);
21594 if(this.singleMode){
21595 this.setValue(this.formatDate(this.viewDate));
21606 //Roo.log(className);
21607 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21608 var day = parseInt(html, 10) || 1;
21609 var year = (this.viewDate || new Date()).getUTCFullYear(),
21610 month = (this.viewDate || new Date()).getUTCMonth();
21612 if (className.indexOf('old') > -1) {
21619 } else if (className.indexOf('new') > -1) {
21627 //Roo.log([year,month,day]);
21628 this.date = this.UTCDate(year, month, day,0,0,0,0);
21629 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21631 //Roo.log(this.formatDate(this.date));
21632 this.setValue(this.formatDate(this.date));
21639 setStartDate: function(startDate)
21641 this.startDate = startDate || -Infinity;
21642 if (this.startDate !== -Infinity) {
21643 this.startDate = this.parseDate(this.startDate);
21646 this.updateNavArrows();
21649 setEndDate: function(endDate)
21651 this.endDate = endDate || Infinity;
21652 if (this.endDate !== Infinity) {
21653 this.endDate = this.parseDate(this.endDate);
21656 this.updateNavArrows();
21659 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21661 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21662 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21663 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21665 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21666 return parseInt(d, 10);
21669 this.updateNavArrows();
21672 updateNavArrows: function()
21674 if(this.singleMode){
21678 var d = new Date(this.viewDate),
21679 year = d.getUTCFullYear(),
21680 month = d.getUTCMonth();
21682 Roo.each(this.picker().select('.prev', true).elements, function(v){
21684 switch (this.viewMode) {
21687 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21693 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21700 Roo.each(this.picker().select('.next', true).elements, function(v){
21702 switch (this.viewMode) {
21705 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21711 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21719 moveMonth: function(date, dir)
21724 var new_date = new Date(date.valueOf()),
21725 day = new_date.getUTCDate(),
21726 month = new_date.getUTCMonth(),
21727 mag = Math.abs(dir),
21729 dir = dir > 0 ? 1 : -1;
21732 // If going back one month, make sure month is not current month
21733 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21735 return new_date.getUTCMonth() == month;
21737 // If going forward one month, make sure month is as expected
21738 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21740 return new_date.getUTCMonth() != new_month;
21742 new_month = month + dir;
21743 new_date.setUTCMonth(new_month);
21744 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21745 if (new_month < 0 || new_month > 11) {
21746 new_month = (new_month + 12) % 12;
21749 // For magnitudes >1, move one month at a time...
21750 for (var i=0; i<mag; i++) {
21751 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21752 new_date = this.moveMonth(new_date, dir);
21754 // ...then reset the day, keeping it in the new month
21755 new_month = new_date.getUTCMonth();
21756 new_date.setUTCDate(day);
21758 return new_month != new_date.getUTCMonth();
21761 // Common date-resetting loop -- if date is beyond end of month, make it
21764 new_date.setUTCDate(--day);
21765 new_date.setUTCMonth(new_month);
21770 moveYear: function(date, dir)
21772 return this.moveMonth(date, dir*12);
21775 dateWithinRange: function(date)
21777 return date >= this.startDate && date <= this.endDate;
21783 this.picker().remove();
21786 validateValue : function(value)
21788 if(this.getVisibilityEl().hasClass('hidden')){
21792 if(value.length < 1) {
21793 if(this.allowBlank){
21799 if(value.length < this.minLength){
21802 if(value.length > this.maxLength){
21806 var vt = Roo.form.VTypes;
21807 if(!vt[this.vtype](value, this)){
21811 if(typeof this.validator == "function"){
21812 var msg = this.validator(value);
21818 if(this.regex && !this.regex.test(value)){
21822 if(typeof(this.parseDate(value)) == 'undefined'){
21826 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21830 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21840 this.date = this.viewDate = '';
21842 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21847 Roo.apply(Roo.bootstrap.DateField, {
21858 html: '<i class="fa fa-arrow-left"/>'
21868 html: '<i class="fa fa-arrow-right"/>'
21910 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21911 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21912 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21913 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21914 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21927 navFnc: 'FullYear',
21932 navFnc: 'FullYear',
21937 Roo.apply(Roo.bootstrap.DateField, {
21941 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21945 cls: 'datepicker-days',
21949 cls: 'table-condensed',
21951 Roo.bootstrap.DateField.head,
21955 Roo.bootstrap.DateField.footer
21962 cls: 'datepicker-months',
21966 cls: 'table-condensed',
21968 Roo.bootstrap.DateField.head,
21969 Roo.bootstrap.DateField.content,
21970 Roo.bootstrap.DateField.footer
21977 cls: 'datepicker-years',
21981 cls: 'table-condensed',
21983 Roo.bootstrap.DateField.head,
21984 Roo.bootstrap.DateField.content,
21985 Roo.bootstrap.DateField.footer
22004 * @class Roo.bootstrap.TimeField
22005 * @extends Roo.bootstrap.Input
22006 * Bootstrap DateField class
22010 * Create a new TimeField
22011 * @param {Object} config The config object
22014 Roo.bootstrap.TimeField = function(config){
22015 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22019 * Fires when this field show.
22020 * @param {Roo.bootstrap.DateField} thisthis
22021 * @param {Mixed} date The date value
22026 * Fires when this field hide.
22027 * @param {Roo.bootstrap.DateField} this
22028 * @param {Mixed} date The date value
22033 * Fires when select a date.
22034 * @param {Roo.bootstrap.DateField} this
22035 * @param {Mixed} date The date value
22041 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22044 * @cfg {String} format
22045 * The default time format string which can be overriden for localization support. The format must be
22046 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22050 getAutoCreate : function()
22052 this.after = '<i class="fa far fa-clock"></i>';
22053 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22057 onRender: function(ct, position)
22060 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22062 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22064 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22066 this.pop = this.picker().select('>.datepicker-time',true).first();
22067 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22069 this.picker().on('mousedown', this.onMousedown, this);
22070 this.picker().on('click', this.onClick, this);
22072 this.picker().addClass('datepicker-dropdown');
22077 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22078 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22079 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22080 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22081 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22082 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22086 fireKey: function(e){
22087 if (!this.picker().isVisible()){
22088 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22094 e.preventDefault();
22102 this.onTogglePeriod();
22105 this.onIncrementMinutes();
22108 this.onDecrementMinutes();
22117 onClick: function(e) {
22118 e.stopPropagation();
22119 e.preventDefault();
22122 picker : function()
22124 return this.pickerEl;
22127 fillTime: function()
22129 var time = this.pop.select('tbody', true).first();
22131 time.dom.innerHTML = '';
22146 cls: 'hours-up fa fas fa-chevron-up'
22166 cls: 'minutes-up fa fas fa-chevron-up'
22187 cls: 'timepicker-hour',
22202 cls: 'timepicker-minute',
22217 cls: 'btn btn-primary period',
22239 cls: 'hours-down fa fas fa-chevron-down'
22259 cls: 'minutes-down fa fas fa-chevron-down'
22277 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22284 var hours = this.time.getHours();
22285 var minutes = this.time.getMinutes();
22298 hours = hours - 12;
22302 hours = '0' + hours;
22306 minutes = '0' + minutes;
22309 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22310 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22311 this.pop.select('button', true).first().dom.innerHTML = period;
22317 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22319 var cls = ['bottom'];
22321 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22328 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22332 //this.picker().setXY(20000,20000);
22333 this.picker().addClass(cls.join('-'));
22337 Roo.each(cls, function(c){
22342 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22343 //_this.picker().setTop(_this.inputEl().getHeight());
22347 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22349 //_this.picker().setTop(0 - _this.picker().getHeight());
22354 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22358 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22366 onFocus : function()
22368 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22372 onBlur : function()
22374 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22380 this.picker().show();
22385 this.fireEvent('show', this, this.date);
22390 this.picker().hide();
22393 this.fireEvent('hide', this, this.date);
22396 setTime : function()
22399 this.setValue(this.time.format(this.format));
22401 this.fireEvent('select', this, this.date);
22406 onMousedown: function(e){
22407 e.stopPropagation();
22408 e.preventDefault();
22411 onIncrementHours: function()
22413 Roo.log('onIncrementHours');
22414 this.time = this.time.add(Date.HOUR, 1);
22419 onDecrementHours: function()
22421 Roo.log('onDecrementHours');
22422 this.time = this.time.add(Date.HOUR, -1);
22426 onIncrementMinutes: function()
22428 Roo.log('onIncrementMinutes');
22429 this.time = this.time.add(Date.MINUTE, 1);
22433 onDecrementMinutes: function()
22435 Roo.log('onDecrementMinutes');
22436 this.time = this.time.add(Date.MINUTE, -1);
22440 onTogglePeriod: function()
22442 Roo.log('onTogglePeriod');
22443 this.time = this.time.add(Date.HOUR, 12);
22451 Roo.apply(Roo.bootstrap.TimeField, {
22455 cls: 'datepicker dropdown-menu',
22459 cls: 'datepicker-time',
22463 cls: 'table-condensed',
22492 cls: 'btn btn-info ok',
22520 * @class Roo.bootstrap.MonthField
22521 * @extends Roo.bootstrap.Input
22522 * Bootstrap MonthField class
22524 * @cfg {String} language default en
22527 * Create a new MonthField
22528 * @param {Object} config The config object
22531 Roo.bootstrap.MonthField = function(config){
22532 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22537 * Fires when this field show.
22538 * @param {Roo.bootstrap.MonthField} this
22539 * @param {Mixed} date The date value
22544 * Fires when this field hide.
22545 * @param {Roo.bootstrap.MonthField} this
22546 * @param {Mixed} date The date value
22551 * Fires when select a date.
22552 * @param {Roo.bootstrap.MonthField} this
22553 * @param {String} oldvalue The old value
22554 * @param {String} newvalue The new value
22560 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22562 onRender: function(ct, position)
22565 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22567 this.language = this.language || 'en';
22568 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22569 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22571 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22572 this.isInline = false;
22573 this.isInput = true;
22574 this.component = this.el.select('.add-on', true).first() || false;
22575 this.component = (this.component && this.component.length === 0) ? false : this.component;
22576 this.hasInput = this.component && this.inputEL().length;
22578 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22580 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22582 this.picker().on('mousedown', this.onMousedown, this);
22583 this.picker().on('click', this.onClick, this);
22585 this.picker().addClass('datepicker-dropdown');
22587 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22588 v.setStyle('width', '189px');
22595 if(this.isInline) {
22601 setValue: function(v, suppressEvent)
22603 var o = this.getValue();
22605 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22609 if(suppressEvent !== true){
22610 this.fireEvent('select', this, o, v);
22615 getValue: function()
22620 onClick: function(e)
22622 e.stopPropagation();
22623 e.preventDefault();
22625 var target = e.getTarget();
22627 if(target.nodeName.toLowerCase() === 'i'){
22628 target = Roo.get(target).dom.parentNode;
22631 var nodeName = target.nodeName;
22632 var className = target.className;
22633 var html = target.innerHTML;
22635 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22639 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22641 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22647 picker : function()
22649 return this.pickerEl;
22652 fillMonths: function()
22655 var months = this.picker().select('>.datepicker-months td', true).first();
22657 months.dom.innerHTML = '';
22663 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22666 months.createChild(month);
22675 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22676 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22679 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22680 e.removeClass('active');
22682 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22683 e.addClass('active');
22690 if(this.isInline) {
22694 this.picker().removeClass(['bottom', 'top']);
22696 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22698 * place to the top of element!
22702 this.picker().addClass('top');
22703 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22708 this.picker().addClass('bottom');
22710 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22713 onFocus : function()
22715 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22719 onBlur : function()
22721 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22723 var d = this.inputEl().getValue();
22732 this.picker().show();
22733 this.picker().select('>.datepicker-months', true).first().show();
22737 this.fireEvent('show', this, this.date);
22742 if(this.isInline) {
22745 this.picker().hide();
22746 this.fireEvent('hide', this, this.date);
22750 onMousedown: function(e)
22752 e.stopPropagation();
22753 e.preventDefault();
22758 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22762 fireKey: function(e)
22764 if (!this.picker().isVisible()){
22765 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22776 e.preventDefault();
22780 dir = e.keyCode == 37 ? -1 : 1;
22782 this.vIndex = this.vIndex + dir;
22784 if(this.vIndex < 0){
22788 if(this.vIndex > 11){
22792 if(isNaN(this.vIndex)){
22796 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22802 dir = e.keyCode == 38 ? -1 : 1;
22804 this.vIndex = this.vIndex + dir * 4;
22806 if(this.vIndex < 0){
22810 if(this.vIndex > 11){
22814 if(isNaN(this.vIndex)){
22818 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22823 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22824 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22828 e.preventDefault();
22831 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22832 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22848 this.picker().remove();
22853 Roo.apply(Roo.bootstrap.MonthField, {
22872 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22873 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22878 Roo.apply(Roo.bootstrap.MonthField, {
22882 cls: 'datepicker dropdown-menu roo-dynamic',
22886 cls: 'datepicker-months',
22890 cls: 'table-condensed',
22892 Roo.bootstrap.DateField.content
22912 * @class Roo.bootstrap.CheckBox
22913 * @extends Roo.bootstrap.Input
22914 * Bootstrap CheckBox class
22916 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22917 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22918 * @cfg {String} boxLabel The text that appears beside the checkbox
22919 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22920 * @cfg {Boolean} checked initnal the element
22921 * @cfg {Boolean} inline inline the element (default false)
22922 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22923 * @cfg {String} tooltip label tooltip
22926 * Create a new CheckBox
22927 * @param {Object} config The config object
22930 Roo.bootstrap.CheckBox = function(config){
22931 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22936 * Fires when the element is checked or unchecked.
22937 * @param {Roo.bootstrap.CheckBox} this This input
22938 * @param {Boolean} checked The new checked value
22943 * Fires when the element is click.
22944 * @param {Roo.bootstrap.CheckBox} this This input
22951 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22953 inputType: 'checkbox',
22962 // checkbox success does not make any sense really..
22967 getAutoCreate : function()
22969 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22975 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22978 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22984 type : this.inputType,
22985 value : this.inputValue,
22986 cls : 'roo-' + this.inputType, //'form-box',
22987 placeholder : this.placeholder || ''
22991 if(this.inputType != 'radio'){
22995 cls : 'roo-hidden-value',
22996 value : this.checked ? this.inputValue : this.valueOff
23001 if (this.weight) { // Validity check?
23002 cfg.cls += " " + this.inputType + "-" + this.weight;
23005 if (this.disabled) {
23006 input.disabled=true;
23010 input.checked = this.checked;
23015 input.name = this.name;
23017 if(this.inputType != 'radio'){
23018 hidden.name = this.name;
23019 input.name = '_hidden_' + this.name;
23024 input.cls += ' input-' + this.size;
23029 ['xs','sm','md','lg'].map(function(size){
23030 if (settings[size]) {
23031 cfg.cls += ' col-' + size + '-' + settings[size];
23035 var inputblock = input;
23037 if (this.before || this.after) {
23040 cls : 'input-group',
23045 inputblock.cn.push({
23047 cls : 'input-group-addon',
23052 inputblock.cn.push(input);
23054 if(this.inputType != 'radio'){
23055 inputblock.cn.push(hidden);
23059 inputblock.cn.push({
23061 cls : 'input-group-addon',
23067 var boxLabelCfg = false;
23073 //'for': id, // box label is handled by onclick - so no for...
23075 html: this.boxLabel
23078 boxLabelCfg.tooltip = this.tooltip;
23084 if (align ==='left' && this.fieldLabel.length) {
23085 // Roo.log("left and has label");
23090 cls : 'control-label',
23091 html : this.fieldLabel
23102 cfg.cn[1].cn.push(boxLabelCfg);
23105 if(this.labelWidth > 12){
23106 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23109 if(this.labelWidth < 13 && this.labelmd == 0){
23110 this.labelmd = this.labelWidth;
23113 if(this.labellg > 0){
23114 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23115 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23118 if(this.labelmd > 0){
23119 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23120 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23123 if(this.labelsm > 0){
23124 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23125 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23128 if(this.labelxs > 0){
23129 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23130 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23133 } else if ( this.fieldLabel.length) {
23134 // Roo.log(" label");
23138 tag: this.boxLabel ? 'span' : 'label',
23140 cls: 'control-label box-input-label',
23141 //cls : 'input-group-addon',
23142 html : this.fieldLabel
23149 cfg.cn.push(boxLabelCfg);
23154 // Roo.log(" no label && no align");
23155 cfg.cn = [ inputblock ] ;
23157 cfg.cn.push(boxLabelCfg);
23165 if(this.inputType != 'radio'){
23166 cfg.cn.push(hidden);
23174 * return the real input element.
23176 inputEl: function ()
23178 return this.el.select('input.roo-' + this.inputType,true).first();
23180 hiddenEl: function ()
23182 return this.el.select('input.roo-hidden-value',true).first();
23185 labelEl: function()
23187 return this.el.select('label.control-label',true).first();
23189 /* depricated... */
23193 return this.labelEl();
23196 boxLabelEl: function()
23198 return this.el.select('label.box-label',true).first();
23201 initEvents : function()
23203 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23205 this.inputEl().on('click', this.onClick, this);
23207 if (this.boxLabel) {
23208 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23211 this.startValue = this.getValue();
23214 Roo.bootstrap.CheckBox.register(this);
23218 onClick : function(e)
23220 if(this.fireEvent('click', this, e) !== false){
23221 this.setChecked(!this.checked);
23226 setChecked : function(state,suppressEvent)
23228 this.startValue = this.getValue();
23230 if(this.inputType == 'radio'){
23232 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23233 e.dom.checked = false;
23236 this.inputEl().dom.checked = true;
23238 this.inputEl().dom.value = this.inputValue;
23240 if(suppressEvent !== true){
23241 this.fireEvent('check', this, true);
23249 this.checked = state;
23251 this.inputEl().dom.checked = state;
23254 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23256 if(suppressEvent !== true){
23257 this.fireEvent('check', this, state);
23263 getValue : function()
23265 if(this.inputType == 'radio'){
23266 return this.getGroupValue();
23269 return this.hiddenEl().dom.value;
23273 getGroupValue : function()
23275 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23279 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23282 setValue : function(v,suppressEvent)
23284 if(this.inputType == 'radio'){
23285 this.setGroupValue(v, suppressEvent);
23289 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23294 setGroupValue : function(v, suppressEvent)
23296 this.startValue = this.getValue();
23298 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23299 e.dom.checked = false;
23301 if(e.dom.value == v){
23302 e.dom.checked = true;
23306 if(suppressEvent !== true){
23307 this.fireEvent('check', this, true);
23315 validate : function()
23317 if(this.getVisibilityEl().hasClass('hidden')){
23323 (this.inputType == 'radio' && this.validateRadio()) ||
23324 (this.inputType == 'checkbox' && this.validateCheckbox())
23330 this.markInvalid();
23334 validateRadio : function()
23336 if(this.getVisibilityEl().hasClass('hidden')){
23340 if(this.allowBlank){
23346 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23347 if(!e.dom.checked){
23359 validateCheckbox : function()
23362 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23363 //return (this.getValue() == this.inputValue) ? true : false;
23366 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23374 for(var i in group){
23375 if(group[i].el.isVisible(true)){
23383 for(var i in group){
23388 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23395 * Mark this field as valid
23397 markValid : function()
23401 this.fireEvent('valid', this);
23403 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23406 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23413 if(this.inputType == 'radio'){
23414 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23415 var fg = e.findParent('.form-group', false, true);
23416 if (Roo.bootstrap.version == 3) {
23417 fg.removeClass([_this.invalidClass, _this.validClass]);
23418 fg.addClass(_this.validClass);
23420 fg.removeClass(['is-valid', 'is-invalid']);
23421 fg.addClass('is-valid');
23429 var fg = this.el.findParent('.form-group', false, true);
23430 if (Roo.bootstrap.version == 3) {
23431 fg.removeClass([this.invalidClass, this.validClass]);
23432 fg.addClass(this.validClass);
23434 fg.removeClass(['is-valid', 'is-invalid']);
23435 fg.addClass('is-valid');
23440 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23446 for(var i in group){
23447 var fg = group[i].el.findParent('.form-group', false, true);
23448 if (Roo.bootstrap.version == 3) {
23449 fg.removeClass([this.invalidClass, this.validClass]);
23450 fg.addClass(this.validClass);
23452 fg.removeClass(['is-valid', 'is-invalid']);
23453 fg.addClass('is-valid');
23459 * Mark this field as invalid
23460 * @param {String} msg The validation message
23462 markInvalid : function(msg)
23464 if(this.allowBlank){
23470 this.fireEvent('invalid', this, msg);
23472 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23475 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23479 label.markInvalid();
23482 if(this.inputType == 'radio'){
23484 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23485 var fg = e.findParent('.form-group', false, true);
23486 if (Roo.bootstrap.version == 3) {
23487 fg.removeClass([_this.invalidClass, _this.validClass]);
23488 fg.addClass(_this.invalidClass);
23490 fg.removeClass(['is-invalid', 'is-valid']);
23491 fg.addClass('is-invalid');
23499 var fg = this.el.findParent('.form-group', false, true);
23500 if (Roo.bootstrap.version == 3) {
23501 fg.removeClass([_this.invalidClass, _this.validClass]);
23502 fg.addClass(_this.invalidClass);
23504 fg.removeClass(['is-invalid', 'is-valid']);
23505 fg.addClass('is-invalid');
23510 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23516 for(var i in group){
23517 var fg = group[i].el.findParent('.form-group', false, true);
23518 if (Roo.bootstrap.version == 3) {
23519 fg.removeClass([_this.invalidClass, _this.validClass]);
23520 fg.addClass(_this.invalidClass);
23522 fg.removeClass(['is-invalid', 'is-valid']);
23523 fg.addClass('is-invalid');
23529 clearInvalid : function()
23531 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23533 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23535 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23537 if (label && label.iconEl) {
23538 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23539 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23543 disable : function()
23545 if(this.inputType != 'radio'){
23546 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23553 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23554 _this.getActionEl().addClass(this.disabledClass);
23555 e.dom.disabled = true;
23559 this.disabled = true;
23560 this.fireEvent("disable", this);
23564 enable : function()
23566 if(this.inputType != 'radio'){
23567 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23574 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23575 _this.getActionEl().removeClass(this.disabledClass);
23576 e.dom.disabled = false;
23580 this.disabled = false;
23581 this.fireEvent("enable", this);
23585 setBoxLabel : function(v)
23590 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23596 Roo.apply(Roo.bootstrap.CheckBox, {
23601 * register a CheckBox Group
23602 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23604 register : function(checkbox)
23606 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23607 this.groups[checkbox.groupId] = {};
23610 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23614 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23618 * fetch a CheckBox Group based on the group ID
23619 * @param {string} the group ID
23620 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23622 get: function(groupId) {
23623 if (typeof(this.groups[groupId]) == 'undefined') {
23627 return this.groups[groupId] ;
23640 * @class Roo.bootstrap.Radio
23641 * @extends Roo.bootstrap.Component
23642 * Bootstrap Radio class
23643 * @cfg {String} boxLabel - the label associated
23644 * @cfg {String} value - the value of radio
23647 * Create a new Radio
23648 * @param {Object} config The config object
23650 Roo.bootstrap.Radio = function(config){
23651 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23655 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23661 getAutoCreate : function()
23665 cls : 'form-group radio',
23670 html : this.boxLabel
23678 initEvents : function()
23680 this.parent().register(this);
23682 this.el.on('click', this.onClick, this);
23686 onClick : function(e)
23688 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23689 this.setChecked(true);
23693 setChecked : function(state, suppressEvent)
23695 this.parent().setValue(this.value, suppressEvent);
23699 setBoxLabel : function(v)
23704 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23719 * @class Roo.bootstrap.SecurePass
23720 * @extends Roo.bootstrap.Input
23721 * Bootstrap SecurePass class
23725 * Create a new SecurePass
23726 * @param {Object} config The config object
23729 Roo.bootstrap.SecurePass = function (config) {
23730 // these go here, so the translation tool can replace them..
23732 PwdEmpty: "Please type a password, and then retype it to confirm.",
23733 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23734 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23735 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23736 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23737 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23738 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23739 TooWeak: "Your password is Too Weak."
23741 this.meterLabel = "Password strength:";
23742 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23743 this.meterClass = [
23744 "roo-password-meter-tooweak",
23745 "roo-password-meter-weak",
23746 "roo-password-meter-medium",
23747 "roo-password-meter-strong",
23748 "roo-password-meter-grey"
23753 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23756 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23758 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23760 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23761 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23762 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23763 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23764 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23765 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23766 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23776 * @cfg {String/Object} Label for the strength meter (defaults to
23777 * 'Password strength:')
23782 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23783 * ['Weak', 'Medium', 'Strong'])
23786 pwdStrengths: false,
23799 initEvents: function ()
23801 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23803 if (this.el.is('input[type=password]') && Roo.isSafari) {
23804 this.el.on('keydown', this.SafariOnKeyDown, this);
23807 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23810 onRender: function (ct, position)
23812 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23813 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23814 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23816 this.trigger.createChild({
23821 cls: 'roo-password-meter-grey col-xs-12',
23824 //width: this.meterWidth + 'px'
23828 cls: 'roo-password-meter-text'
23834 if (this.hideTrigger) {
23835 this.trigger.setDisplayed(false);
23837 this.setSize(this.width || '', this.height || '');
23840 onDestroy: function ()
23842 if (this.trigger) {
23843 this.trigger.removeAllListeners();
23844 this.trigger.remove();
23847 this.wrap.remove();
23849 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23852 checkStrength: function ()
23854 var pwd = this.inputEl().getValue();
23855 if (pwd == this._lastPwd) {
23860 if (this.ClientSideStrongPassword(pwd)) {
23862 } else if (this.ClientSideMediumPassword(pwd)) {
23864 } else if (this.ClientSideWeakPassword(pwd)) {
23870 Roo.log('strength1: ' + strength);
23872 //var pm = this.trigger.child('div/div/div').dom;
23873 var pm = this.trigger.child('div/div');
23874 pm.removeClass(this.meterClass);
23875 pm.addClass(this.meterClass[strength]);
23878 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23880 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23882 this._lastPwd = pwd;
23886 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23888 this._lastPwd = '';
23890 var pm = this.trigger.child('div/div');
23891 pm.removeClass(this.meterClass);
23892 pm.addClass('roo-password-meter-grey');
23895 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23898 this.inputEl().dom.type='password';
23901 validateValue: function (value)
23903 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23906 if (value.length == 0) {
23907 if (this.allowBlank) {
23908 this.clearInvalid();
23912 this.markInvalid(this.errors.PwdEmpty);
23913 this.errorMsg = this.errors.PwdEmpty;
23921 if (!value.match(/[\x21-\x7e]+/)) {
23922 this.markInvalid(this.errors.PwdBadChar);
23923 this.errorMsg = this.errors.PwdBadChar;
23926 if (value.length < 6) {
23927 this.markInvalid(this.errors.PwdShort);
23928 this.errorMsg = this.errors.PwdShort;
23931 if (value.length > 16) {
23932 this.markInvalid(this.errors.PwdLong);
23933 this.errorMsg = this.errors.PwdLong;
23937 if (this.ClientSideStrongPassword(value)) {
23939 } else if (this.ClientSideMediumPassword(value)) {
23941 } else if (this.ClientSideWeakPassword(value)) {
23948 if (strength < 2) {
23949 //this.markInvalid(this.errors.TooWeak);
23950 this.errorMsg = this.errors.TooWeak;
23955 console.log('strength2: ' + strength);
23957 //var pm = this.trigger.child('div/div/div').dom;
23959 var pm = this.trigger.child('div/div');
23960 pm.removeClass(this.meterClass);
23961 pm.addClass(this.meterClass[strength]);
23963 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23965 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23967 this.errorMsg = '';
23971 CharacterSetChecks: function (type)
23974 this.fResult = false;
23977 isctype: function (character, type)
23980 case this.kCapitalLetter:
23981 if (character >= 'A' && character <= 'Z') {
23986 case this.kSmallLetter:
23987 if (character >= 'a' && character <= 'z') {
23993 if (character >= '0' && character <= '9') {
23998 case this.kPunctuation:
23999 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24010 IsLongEnough: function (pwd, size)
24012 return !(pwd == null || isNaN(size) || pwd.length < size);
24015 SpansEnoughCharacterSets: function (word, nb)
24017 if (!this.IsLongEnough(word, nb))
24022 var characterSetChecks = new Array(
24023 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24024 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24027 for (var index = 0; index < word.length; ++index) {
24028 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24029 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24030 characterSetChecks[nCharSet].fResult = true;
24037 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24038 if (characterSetChecks[nCharSet].fResult) {
24043 if (nCharSets < nb) {
24049 ClientSideStrongPassword: function (pwd)
24051 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24054 ClientSideMediumPassword: function (pwd)
24056 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24059 ClientSideWeakPassword: function (pwd)
24061 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24064 })//<script type="text/javascript">
24067 * Based Ext JS Library 1.1.1
24068 * Copyright(c) 2006-2007, Ext JS, LLC.
24074 * @class Roo.HtmlEditorCore
24075 * @extends Roo.Component
24076 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24078 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24081 Roo.HtmlEditorCore = function(config){
24084 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24089 * @event initialize
24090 * Fires when the editor is fully initialized (including the iframe)
24091 * @param {Roo.HtmlEditorCore} this
24096 * Fires when the editor is first receives the focus. Any insertion must wait
24097 * until after this event.
24098 * @param {Roo.HtmlEditorCore} this
24102 * @event beforesync
24103 * Fires before the textarea is updated with content from the editor iframe. Return false
24104 * to cancel the sync.
24105 * @param {Roo.HtmlEditorCore} this
24106 * @param {String} html
24110 * @event beforepush
24111 * Fires before the iframe editor is updated with content from the textarea. Return false
24112 * to cancel the push.
24113 * @param {Roo.HtmlEditorCore} this
24114 * @param {String} html
24119 * Fires when the textarea is updated with content from the editor iframe.
24120 * @param {Roo.HtmlEditorCore} this
24121 * @param {String} html
24126 * Fires when the iframe editor is updated with content from the textarea.
24127 * @param {Roo.HtmlEditorCore} this
24128 * @param {String} html
24133 * @event editorevent
24134 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24135 * @param {Roo.HtmlEditorCore} this
24141 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24143 // defaults : white / black...
24144 this.applyBlacklists();
24151 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24155 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24161 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24166 * @cfg {Number} height (in pixels)
24170 * @cfg {Number} width (in pixels)
24175 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24178 stylesheets: false,
24183 // private properties
24184 validationEvent : false,
24186 initialized : false,
24188 sourceEditMode : false,
24189 onFocus : Roo.emptyFn,
24191 hideMode:'offsets',
24195 // blacklist + whitelisted elements..
24202 * Protected method that will not generally be called directly. It
24203 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24204 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24206 getDocMarkup : function(){
24210 // inherit styels from page...??
24211 if (this.stylesheets === false) {
24213 Roo.get(document.head).select('style').each(function(node) {
24214 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24217 Roo.get(document.head).select('link').each(function(node) {
24218 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24221 } else if (!this.stylesheets.length) {
24223 st = '<style type="text/css">' +
24224 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24227 for (var i in this.stylesheets) {
24228 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24233 st += '<style type="text/css">' +
24234 'IMG { cursor: pointer } ' +
24237 var cls = 'roo-htmleditor-body';
24239 if(this.bodyCls.length){
24240 cls += ' ' + this.bodyCls;
24243 return '<html><head>' + st +
24244 //<style type="text/css">' +
24245 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24247 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24251 onRender : function(ct, position)
24254 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24255 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24258 this.el.dom.style.border = '0 none';
24259 this.el.dom.setAttribute('tabIndex', -1);
24260 this.el.addClass('x-hidden hide');
24264 if(Roo.isIE){ // fix IE 1px bogus margin
24265 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24269 this.frameId = Roo.id();
24273 var iframe = this.owner.wrap.createChild({
24275 cls: 'form-control', // bootstrap..
24277 name: this.frameId,
24278 frameBorder : 'no',
24279 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24284 this.iframe = iframe.dom;
24286 this.assignDocWin();
24288 this.doc.designMode = 'on';
24291 this.doc.write(this.getDocMarkup());
24295 var task = { // must defer to wait for browser to be ready
24297 //console.log("run task?" + this.doc.readyState);
24298 this.assignDocWin();
24299 if(this.doc.body || this.doc.readyState == 'complete'){
24301 this.doc.designMode="on";
24305 Roo.TaskMgr.stop(task);
24306 this.initEditor.defer(10, this);
24313 Roo.TaskMgr.start(task);
24318 onResize : function(w, h)
24320 Roo.log('resize: ' +w + ',' + h );
24321 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24325 if(typeof w == 'number'){
24327 this.iframe.style.width = w + 'px';
24329 if(typeof h == 'number'){
24331 this.iframe.style.height = h + 'px';
24333 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24340 * Toggles the editor between standard and source edit mode.
24341 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24343 toggleSourceEdit : function(sourceEditMode){
24345 this.sourceEditMode = sourceEditMode === true;
24347 if(this.sourceEditMode){
24349 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24352 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24353 //this.iframe.className = '';
24356 //this.setSize(this.owner.wrap.getSize());
24357 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24364 * Protected method that will not generally be called directly. If you need/want
24365 * custom HTML cleanup, this is the method you should override.
24366 * @param {String} html The HTML to be cleaned
24367 * return {String} The cleaned HTML
24369 cleanHtml : function(html){
24370 html = String(html);
24371 if(html.length > 5){
24372 if(Roo.isSafari){ // strip safari nonsense
24373 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24376 if(html == ' '){
24383 * HTML Editor -> Textarea
24384 * Protected method that will not generally be called directly. Syncs the contents
24385 * of the editor iframe with the textarea.
24387 syncValue : function(){
24388 if(this.initialized){
24389 var bd = (this.doc.body || this.doc.documentElement);
24390 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24391 var html = bd.innerHTML;
24393 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24394 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24396 html = '<div style="'+m[0]+'">' + html + '</div>';
24399 html = this.cleanHtml(html);
24400 // fix up the special chars.. normaly like back quotes in word...
24401 // however we do not want to do this with chinese..
24402 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24404 var cc = match.charCodeAt();
24406 // Get the character value, handling surrogate pairs
24407 if (match.length == 2) {
24408 // It's a surrogate pair, calculate the Unicode code point
24409 var high = match.charCodeAt(0) - 0xD800;
24410 var low = match.charCodeAt(1) - 0xDC00;
24411 cc = (high * 0x400) + low + 0x10000;
24413 (cc >= 0x4E00 && cc < 0xA000 ) ||
24414 (cc >= 0x3400 && cc < 0x4E00 ) ||
24415 (cc >= 0xf900 && cc < 0xfb00 )
24420 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24421 return "&#" + cc + ";";
24428 if(this.owner.fireEvent('beforesync', this, html) !== false){
24429 this.el.dom.value = html;
24430 this.owner.fireEvent('sync', this, html);
24436 * Protected method that will not generally be called directly. Pushes the value of the textarea
24437 * into the iframe editor.
24439 pushValue : function(){
24440 if(this.initialized){
24441 var v = this.el.dom.value.trim();
24443 // if(v.length < 1){
24447 if(this.owner.fireEvent('beforepush', this, v) !== false){
24448 var d = (this.doc.body || this.doc.documentElement);
24450 this.cleanUpPaste();
24451 this.el.dom.value = d.innerHTML;
24452 this.owner.fireEvent('push', this, v);
24458 deferFocus : function(){
24459 this.focus.defer(10, this);
24463 focus : function(){
24464 if(this.win && !this.sourceEditMode){
24471 assignDocWin: function()
24473 var iframe = this.iframe;
24476 this.doc = iframe.contentWindow.document;
24477 this.win = iframe.contentWindow;
24479 // if (!Roo.get(this.frameId)) {
24482 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24483 // this.win = Roo.get(this.frameId).dom.contentWindow;
24485 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24489 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24490 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24495 initEditor : function(){
24496 //console.log("INIT EDITOR");
24497 this.assignDocWin();
24501 this.doc.designMode="on";
24503 this.doc.write(this.getDocMarkup());
24506 var dbody = (this.doc.body || this.doc.documentElement);
24507 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24508 // this copies styles from the containing element into thsi one..
24509 // not sure why we need all of this..
24510 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24512 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24513 //ss['background-attachment'] = 'fixed'; // w3c
24514 dbody.bgProperties = 'fixed'; // ie
24515 //Roo.DomHelper.applyStyles(dbody, ss);
24516 Roo.EventManager.on(this.doc, {
24517 //'mousedown': this.onEditorEvent,
24518 'mouseup': this.onEditorEvent,
24519 'dblclick': this.onEditorEvent,
24520 'click': this.onEditorEvent,
24521 'keyup': this.onEditorEvent,
24526 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24528 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24529 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24531 this.initialized = true;
24533 this.owner.fireEvent('initialize', this);
24538 onDestroy : function(){
24544 //for (var i =0; i < this.toolbars.length;i++) {
24545 // // fixme - ask toolbars for heights?
24546 // this.toolbars[i].onDestroy();
24549 //this.wrap.dom.innerHTML = '';
24550 //this.wrap.remove();
24555 onFirstFocus : function(){
24557 this.assignDocWin();
24560 this.activated = true;
24563 if(Roo.isGecko){ // prevent silly gecko errors
24565 var s = this.win.getSelection();
24566 if(!s.focusNode || s.focusNode.nodeType != 3){
24567 var r = s.getRangeAt(0);
24568 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24573 this.execCmd('useCSS', true);
24574 this.execCmd('styleWithCSS', false);
24577 this.owner.fireEvent('activate', this);
24581 adjustFont: function(btn){
24582 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24583 //if(Roo.isSafari){ // safari
24586 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24587 if(Roo.isSafari){ // safari
24588 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24589 v = (v < 10) ? 10 : v;
24590 v = (v > 48) ? 48 : v;
24591 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24596 v = Math.max(1, v+adjust);
24598 this.execCmd('FontSize', v );
24601 onEditorEvent : function(e)
24603 this.owner.fireEvent('editorevent', this, e);
24604 // this.updateToolbar();
24605 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24608 insertTag : function(tg)
24610 // could be a bit smarter... -> wrap the current selected tRoo..
24611 if (tg.toLowerCase() == 'span' ||
24612 tg.toLowerCase() == 'code' ||
24613 tg.toLowerCase() == 'sup' ||
24614 tg.toLowerCase() == 'sub'
24617 range = this.createRange(this.getSelection());
24618 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24619 wrappingNode.appendChild(range.extractContents());
24620 range.insertNode(wrappingNode);
24627 this.execCmd("formatblock", tg);
24631 insertText : function(txt)
24635 var range = this.createRange();
24636 range.deleteContents();
24637 //alert(Sender.getAttribute('label'));
24639 range.insertNode(this.doc.createTextNode(txt));
24645 * Executes a Midas editor command on the editor document and performs necessary focus and
24646 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24647 * @param {String} cmd The Midas command
24648 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24650 relayCmd : function(cmd, value){
24652 this.execCmd(cmd, value);
24653 this.owner.fireEvent('editorevent', this);
24654 //this.updateToolbar();
24655 this.owner.deferFocus();
24659 * Executes a Midas editor command directly on the editor document.
24660 * For visual commands, you should use {@link #relayCmd} instead.
24661 * <b>This should only be called after the editor is initialized.</b>
24662 * @param {String} cmd The Midas command
24663 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24665 execCmd : function(cmd, value){
24666 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24673 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24675 * @param {String} text | dom node..
24677 insertAtCursor : function(text)
24680 if(!this.activated){
24686 var r = this.doc.selection.createRange();
24697 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24701 // from jquery ui (MIT licenced)
24703 var win = this.win;
24705 if (win.getSelection && win.getSelection().getRangeAt) {
24706 range = win.getSelection().getRangeAt(0);
24707 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24708 range.insertNode(node);
24709 } else if (win.document.selection && win.document.selection.createRange) {
24710 // no firefox support
24711 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24712 win.document.selection.createRange().pasteHTML(txt);
24714 // no firefox support
24715 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24716 this.execCmd('InsertHTML', txt);
24725 mozKeyPress : function(e){
24727 var c = e.getCharCode(), cmd;
24730 c = String.fromCharCode(c).toLowerCase();
24744 this.cleanUpPaste.defer(100, this);
24752 e.preventDefault();
24760 fixKeys : function(){ // load time branching for fastest keydown performance
24762 return function(e){
24763 var k = e.getKey(), r;
24766 r = this.doc.selection.createRange();
24769 r.pasteHTML('    ');
24776 r = this.doc.selection.createRange();
24778 var target = r.parentElement();
24779 if(!target || target.tagName.toLowerCase() != 'li'){
24781 r.pasteHTML('<br />');
24787 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24788 this.cleanUpPaste.defer(100, this);
24794 }else if(Roo.isOpera){
24795 return function(e){
24796 var k = e.getKey();
24800 this.execCmd('InsertHTML','    ');
24803 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24804 this.cleanUpPaste.defer(100, this);
24809 }else if(Roo.isSafari){
24810 return function(e){
24811 var k = e.getKey();
24815 this.execCmd('InsertText','\t');
24819 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24820 this.cleanUpPaste.defer(100, this);
24828 getAllAncestors: function()
24830 var p = this.getSelectedNode();
24833 a.push(p); // push blank onto stack..
24834 p = this.getParentElement();
24838 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24842 a.push(this.doc.body);
24846 lastSelNode : false,
24849 getSelection : function()
24851 this.assignDocWin();
24852 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24855 getSelectedNode: function()
24857 // this may only work on Gecko!!!
24859 // should we cache this!!!!
24864 var range = this.createRange(this.getSelection()).cloneRange();
24867 var parent = range.parentElement();
24869 var testRange = range.duplicate();
24870 testRange.moveToElementText(parent);
24871 if (testRange.inRange(range)) {
24874 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24877 parent = parent.parentElement;
24882 // is ancestor a text element.
24883 var ac = range.commonAncestorContainer;
24884 if (ac.nodeType == 3) {
24885 ac = ac.parentNode;
24888 var ar = ac.childNodes;
24891 var other_nodes = [];
24892 var has_other_nodes = false;
24893 for (var i=0;i<ar.length;i++) {
24894 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24897 // fullly contained node.
24899 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24904 // probably selected..
24905 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24906 other_nodes.push(ar[i]);
24910 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24915 has_other_nodes = true;
24917 if (!nodes.length && other_nodes.length) {
24918 nodes= other_nodes;
24920 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24926 createRange: function(sel)
24928 // this has strange effects when using with
24929 // top toolbar - not sure if it's a great idea.
24930 //this.editor.contentWindow.focus();
24931 if (typeof sel != "undefined") {
24933 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24935 return this.doc.createRange();
24938 return this.doc.createRange();
24941 getParentElement: function()
24944 this.assignDocWin();
24945 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24947 var range = this.createRange(sel);
24950 var p = range.commonAncestorContainer;
24951 while (p.nodeType == 3) { // text node
24962 * Range intersection.. the hard stuff...
24966 * [ -- selected range --- ]
24970 * if end is before start or hits it. fail.
24971 * if start is after end or hits it fail.
24973 * if either hits (but other is outside. - then it's not
24979 // @see http://www.thismuchiknow.co.uk/?p=64.
24980 rangeIntersectsNode : function(range, node)
24982 var nodeRange = node.ownerDocument.createRange();
24984 nodeRange.selectNode(node);
24986 nodeRange.selectNodeContents(node);
24989 var rangeStartRange = range.cloneRange();
24990 rangeStartRange.collapse(true);
24992 var rangeEndRange = range.cloneRange();
24993 rangeEndRange.collapse(false);
24995 var nodeStartRange = nodeRange.cloneRange();
24996 nodeStartRange.collapse(true);
24998 var nodeEndRange = nodeRange.cloneRange();
24999 nodeEndRange.collapse(false);
25001 return rangeStartRange.compareBoundaryPoints(
25002 Range.START_TO_START, nodeEndRange) == -1 &&
25003 rangeEndRange.compareBoundaryPoints(
25004 Range.START_TO_START, nodeStartRange) == 1;
25008 rangeCompareNode : function(range, node)
25010 var nodeRange = node.ownerDocument.createRange();
25012 nodeRange.selectNode(node);
25014 nodeRange.selectNodeContents(node);
25018 range.collapse(true);
25020 nodeRange.collapse(true);
25022 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25023 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25025 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25027 var nodeIsBefore = ss == 1;
25028 var nodeIsAfter = ee == -1;
25030 if (nodeIsBefore && nodeIsAfter) {
25033 if (!nodeIsBefore && nodeIsAfter) {
25034 return 1; //right trailed.
25037 if (nodeIsBefore && !nodeIsAfter) {
25038 return 2; // left trailed.
25044 // private? - in a new class?
25045 cleanUpPaste : function()
25047 // cleans up the whole document..
25048 Roo.log('cleanuppaste');
25050 this.cleanUpChildren(this.doc.body);
25051 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25052 if (clean != this.doc.body.innerHTML) {
25053 this.doc.body.innerHTML = clean;
25058 cleanWordChars : function(input) {// change the chars to hex code
25059 var he = Roo.HtmlEditorCore;
25061 var output = input;
25062 Roo.each(he.swapCodes, function(sw) {
25063 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25065 output = output.replace(swapper, sw[1]);
25072 cleanUpChildren : function (n)
25074 if (!n.childNodes.length) {
25077 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25078 this.cleanUpChild(n.childNodes[i]);
25085 cleanUpChild : function (node)
25088 //console.log(node);
25089 if (node.nodeName == "#text") {
25090 // clean up silly Windows -- stuff?
25093 if (node.nodeName == "#comment") {
25094 node.parentNode.removeChild(node);
25095 // clean up silly Windows -- stuff?
25098 var lcname = node.tagName.toLowerCase();
25099 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25100 // whitelist of tags..
25102 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25104 node.parentNode.removeChild(node);
25109 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25111 // spans with no attributes - just remove them..
25112 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25113 remove_keep_children = true;
25116 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25117 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25119 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25120 // remove_keep_children = true;
25123 if (remove_keep_children) {
25124 this.cleanUpChildren(node);
25125 // inserts everything just before this node...
25126 while (node.childNodes.length) {
25127 var cn = node.childNodes[0];
25128 node.removeChild(cn);
25129 node.parentNode.insertBefore(cn, node);
25131 node.parentNode.removeChild(node);
25135 if (!node.attributes || !node.attributes.length) {
25140 this.cleanUpChildren(node);
25144 function cleanAttr(n,v)
25147 if (v.match(/^\./) || v.match(/^\//)) {
25150 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25153 if (v.match(/^#/)) {
25156 if (v.match(/^\{/)) { // allow template editing.
25159 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25160 node.removeAttribute(n);
25164 var cwhite = this.cwhite;
25165 var cblack = this.cblack;
25167 function cleanStyle(n,v)
25169 if (v.match(/expression/)) { //XSS?? should we even bother..
25170 node.removeAttribute(n);
25174 var parts = v.split(/;/);
25177 Roo.each(parts, function(p) {
25178 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25182 var l = p.split(':').shift().replace(/\s+/g,'');
25183 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25185 if ( cwhite.length && cblack.indexOf(l) > -1) {
25186 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25187 //node.removeAttribute(n);
25191 // only allow 'c whitelisted system attributes'
25192 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25193 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25194 //node.removeAttribute(n);
25204 if (clean.length) {
25205 node.setAttribute(n, clean.join(';'));
25207 node.removeAttribute(n);
25213 for (var i = node.attributes.length-1; i > -1 ; i--) {
25214 var a = node.attributes[i];
25217 if (a.name.toLowerCase().substr(0,2)=='on') {
25218 node.removeAttribute(a.name);
25221 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25222 node.removeAttribute(a.name);
25225 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25226 cleanAttr(a.name,a.value); // fixme..
25229 if (a.name == 'style') {
25230 cleanStyle(a.name,a.value);
25233 /// clean up MS crap..
25234 // tecnically this should be a list of valid class'es..
25237 if (a.name == 'class') {
25238 if (a.value.match(/^Mso/)) {
25239 node.removeAttribute('class');
25242 if (a.value.match(/^body$/)) {
25243 node.removeAttribute('class');
25254 this.cleanUpChildren(node);
25260 * Clean up MS wordisms...
25262 cleanWord : function(node)
25265 this.cleanWord(this.doc.body);
25270 node.nodeName == 'SPAN' &&
25271 !node.hasAttributes() &&
25272 node.childNodes.length == 1 &&
25273 node.firstChild.nodeName == "#text"
25275 var textNode = node.firstChild;
25276 node.removeChild(textNode);
25277 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25278 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25280 node.parentNode.insertBefore(textNode, node);
25281 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25282 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25284 node.parentNode.removeChild(node);
25287 if (node.nodeName == "#text") {
25288 // clean up silly Windows -- stuff?
25291 if (node.nodeName == "#comment") {
25292 node.parentNode.removeChild(node);
25293 // clean up silly Windows -- stuff?
25297 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25298 node.parentNode.removeChild(node);
25301 //Roo.log(node.tagName);
25302 // remove - but keep children..
25303 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25304 //Roo.log('-- removed');
25305 while (node.childNodes.length) {
25306 var cn = node.childNodes[0];
25307 node.removeChild(cn);
25308 node.parentNode.insertBefore(cn, node);
25309 // move node to parent - and clean it..
25310 this.cleanWord(cn);
25312 node.parentNode.removeChild(node);
25313 /// no need to iterate chidlren = it's got none..
25314 //this.iterateChildren(node, this.cleanWord);
25318 if (node.className.length) {
25320 var cn = node.className.split(/\W+/);
25322 Roo.each(cn, function(cls) {
25323 if (cls.match(/Mso[a-zA-Z]+/)) {
25328 node.className = cna.length ? cna.join(' ') : '';
25330 node.removeAttribute("class");
25334 if (node.hasAttribute("lang")) {
25335 node.removeAttribute("lang");
25338 if (node.hasAttribute("style")) {
25340 var styles = node.getAttribute("style").split(";");
25342 Roo.each(styles, function(s) {
25343 if (!s.match(/:/)) {
25346 var kv = s.split(":");
25347 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25350 // what ever is left... we allow.
25353 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25354 if (!nstyle.length) {
25355 node.removeAttribute('style');
25358 this.iterateChildren(node, this.cleanWord);
25364 * iterateChildren of a Node, calling fn each time, using this as the scole..
25365 * @param {DomNode} node node to iterate children of.
25366 * @param {Function} fn method of this class to call on each item.
25368 iterateChildren : function(node, fn)
25370 if (!node.childNodes.length) {
25373 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25374 fn.call(this, node.childNodes[i])
25380 * cleanTableWidths.
25382 * Quite often pasting from word etc.. results in tables with column and widths.
25383 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25386 cleanTableWidths : function(node)
25391 this.cleanTableWidths(this.doc.body);
25396 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25399 Roo.log(node.tagName);
25400 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25401 this.iterateChildren(node, this.cleanTableWidths);
25404 if (node.hasAttribute('width')) {
25405 node.removeAttribute('width');
25409 if (node.hasAttribute("style")) {
25412 var styles = node.getAttribute("style").split(";");
25414 Roo.each(styles, function(s) {
25415 if (!s.match(/:/)) {
25418 var kv = s.split(":");
25419 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25422 // what ever is left... we allow.
25425 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25426 if (!nstyle.length) {
25427 node.removeAttribute('style');
25431 this.iterateChildren(node, this.cleanTableWidths);
25439 domToHTML : function(currentElement, depth, nopadtext) {
25441 depth = depth || 0;
25442 nopadtext = nopadtext || false;
25444 if (!currentElement) {
25445 return this.domToHTML(this.doc.body);
25448 //Roo.log(currentElement);
25450 var allText = false;
25451 var nodeName = currentElement.nodeName;
25452 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25454 if (nodeName == '#text') {
25456 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25461 if (nodeName != 'BODY') {
25464 // Prints the node tagName, such as <A>, <IMG>, etc
25467 for(i = 0; i < currentElement.attributes.length;i++) {
25469 var aname = currentElement.attributes.item(i).name;
25470 if (!currentElement.attributes.item(i).value.length) {
25473 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25476 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25485 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25488 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25493 // Traverse the tree
25495 var currentElementChild = currentElement.childNodes.item(i);
25496 var allText = true;
25497 var innerHTML = '';
25499 while (currentElementChild) {
25500 // Formatting code (indent the tree so it looks nice on the screen)
25501 var nopad = nopadtext;
25502 if (lastnode == 'SPAN') {
25506 if (currentElementChild.nodeName == '#text') {
25507 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25508 toadd = nopadtext ? toadd : toadd.trim();
25509 if (!nopad && toadd.length > 80) {
25510 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25512 innerHTML += toadd;
25515 currentElementChild = currentElement.childNodes.item(i);
25521 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25523 // Recursively traverse the tree structure of the child node
25524 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25525 lastnode = currentElementChild.nodeName;
25527 currentElementChild=currentElement.childNodes.item(i);
25533 // The remaining code is mostly for formatting the tree
25534 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25539 ret+= "</"+tagName+">";
25545 applyBlacklists : function()
25547 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25548 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25552 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25553 if (b.indexOf(tag) > -1) {
25556 this.white.push(tag);
25560 Roo.each(w, function(tag) {
25561 if (b.indexOf(tag) > -1) {
25564 if (this.white.indexOf(tag) > -1) {
25567 this.white.push(tag);
25572 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25573 if (w.indexOf(tag) > -1) {
25576 this.black.push(tag);
25580 Roo.each(b, function(tag) {
25581 if (w.indexOf(tag) > -1) {
25584 if (this.black.indexOf(tag) > -1) {
25587 this.black.push(tag);
25592 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25593 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25597 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25598 if (b.indexOf(tag) > -1) {
25601 this.cwhite.push(tag);
25605 Roo.each(w, function(tag) {
25606 if (b.indexOf(tag) > -1) {
25609 if (this.cwhite.indexOf(tag) > -1) {
25612 this.cwhite.push(tag);
25617 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25618 if (w.indexOf(tag) > -1) {
25621 this.cblack.push(tag);
25625 Roo.each(b, function(tag) {
25626 if (w.indexOf(tag) > -1) {
25629 if (this.cblack.indexOf(tag) > -1) {
25632 this.cblack.push(tag);
25637 setStylesheets : function(stylesheets)
25639 if(typeof(stylesheets) == 'string'){
25640 Roo.get(this.iframe.contentDocument.head).createChild({
25642 rel : 'stylesheet',
25651 Roo.each(stylesheets, function(s) {
25656 Roo.get(_this.iframe.contentDocument.head).createChild({
25658 rel : 'stylesheet',
25667 removeStylesheets : function()
25671 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25676 setStyle : function(style)
25678 Roo.get(this.iframe.contentDocument.head).createChild({
25687 // hide stuff that is not compatible
25701 * @event specialkey
25705 * @cfg {String} fieldClass @hide
25708 * @cfg {String} focusClass @hide
25711 * @cfg {String} autoCreate @hide
25714 * @cfg {String} inputType @hide
25717 * @cfg {String} invalidClass @hide
25720 * @cfg {String} invalidText @hide
25723 * @cfg {String} msgFx @hide
25726 * @cfg {String} validateOnBlur @hide
25730 Roo.HtmlEditorCore.white = [
25731 'area', 'br', 'img', 'input', 'hr', 'wbr',
25733 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25734 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25735 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25736 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25737 'table', 'ul', 'xmp',
25739 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25742 'dir', 'menu', 'ol', 'ul', 'dl',
25748 Roo.HtmlEditorCore.black = [
25749 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25751 'base', 'basefont', 'bgsound', 'blink', 'body',
25752 'frame', 'frameset', 'head', 'html', 'ilayer',
25753 'iframe', 'layer', 'link', 'meta', 'object',
25754 'script', 'style' ,'title', 'xml' // clean later..
25756 Roo.HtmlEditorCore.clean = [
25757 'script', 'style', 'title', 'xml'
25759 Roo.HtmlEditorCore.remove = [
25764 Roo.HtmlEditorCore.ablack = [
25768 Roo.HtmlEditorCore.aclean = [
25769 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25773 Roo.HtmlEditorCore.pwhite= [
25774 'http', 'https', 'mailto'
25777 // white listed style attributes.
25778 Roo.HtmlEditorCore.cwhite= [
25779 // 'text-align', /// default is to allow most things..
25785 // black listed style attributes.
25786 Roo.HtmlEditorCore.cblack= [
25787 // 'font-size' -- this can be set by the project
25791 Roo.HtmlEditorCore.swapCodes =[
25810 * @class Roo.bootstrap.HtmlEditor
25811 * @extends Roo.bootstrap.TextArea
25812 * Bootstrap HtmlEditor class
25815 * Create a new HtmlEditor
25816 * @param {Object} config The config object
25819 Roo.bootstrap.HtmlEditor = function(config){
25820 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25821 if (!this.toolbars) {
25822 this.toolbars = [];
25825 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25828 * @event initialize
25829 * Fires when the editor is fully initialized (including the iframe)
25830 * @param {HtmlEditor} this
25835 * Fires when the editor is first receives the focus. Any insertion must wait
25836 * until after this event.
25837 * @param {HtmlEditor} this
25841 * @event beforesync
25842 * Fires before the textarea is updated with content from the editor iframe. Return false
25843 * to cancel the sync.
25844 * @param {HtmlEditor} this
25845 * @param {String} html
25849 * @event beforepush
25850 * Fires before the iframe editor is updated with content from the textarea. Return false
25851 * to cancel the push.
25852 * @param {HtmlEditor} this
25853 * @param {String} html
25858 * Fires when the textarea is updated with content from the editor iframe.
25859 * @param {HtmlEditor} this
25860 * @param {String} html
25865 * Fires when the iframe editor is updated with content from the textarea.
25866 * @param {HtmlEditor} this
25867 * @param {String} html
25871 * @event editmodechange
25872 * Fires when the editor switches edit modes
25873 * @param {HtmlEditor} this
25874 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25876 editmodechange: true,
25878 * @event editorevent
25879 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25880 * @param {HtmlEditor} this
25884 * @event firstfocus
25885 * Fires when on first focus - needed by toolbars..
25886 * @param {HtmlEditor} this
25891 * Auto save the htmlEditor value as a file into Events
25892 * @param {HtmlEditor} this
25896 * @event savedpreview
25897 * preview the saved version of htmlEditor
25898 * @param {HtmlEditor} this
25905 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25909 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25914 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25919 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25924 * @cfg {Number} height (in pixels)
25928 * @cfg {Number} width (in pixels)
25933 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25936 stylesheets: false,
25941 // private properties
25942 validationEvent : false,
25944 initialized : false,
25947 onFocus : Roo.emptyFn,
25949 hideMode:'offsets',
25951 tbContainer : false,
25955 toolbarContainer :function() {
25956 return this.wrap.select('.x-html-editor-tb',true).first();
25960 * Protected method that will not generally be called directly. It
25961 * is called when the editor creates its toolbar. Override this method if you need to
25962 * add custom toolbar buttons.
25963 * @param {HtmlEditor} editor
25965 createToolbar : function(){
25966 Roo.log('renewing');
25967 Roo.log("create toolbars");
25969 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25970 this.toolbars[0].render(this.toolbarContainer());
25974 // if (!editor.toolbars || !editor.toolbars.length) {
25975 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25978 // for (var i =0 ; i < editor.toolbars.length;i++) {
25979 // editor.toolbars[i] = Roo.factory(
25980 // typeof(editor.toolbars[i]) == 'string' ?
25981 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25982 // Roo.bootstrap.HtmlEditor);
25983 // editor.toolbars[i].init(editor);
25989 onRender : function(ct, position)
25991 // Roo.log("Call onRender: " + this.xtype);
25993 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25995 this.wrap = this.inputEl().wrap({
25996 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25999 this.editorcore.onRender(ct, position);
26001 if (this.resizable) {
26002 this.resizeEl = new Roo.Resizable(this.wrap, {
26006 minHeight : this.height,
26007 height: this.height,
26008 handles : this.resizable,
26011 resize : function(r, w, h) {
26012 _t.onResize(w,h); // -something
26018 this.createToolbar(this);
26021 if(!this.width && this.resizable){
26022 this.setSize(this.wrap.getSize());
26024 if (this.resizeEl) {
26025 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26026 // should trigger onReize..
26032 onResize : function(w, h)
26034 Roo.log('resize: ' +w + ',' + h );
26035 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26039 if(this.inputEl() ){
26040 if(typeof w == 'number'){
26041 var aw = w - this.wrap.getFrameWidth('lr');
26042 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26045 if(typeof h == 'number'){
26046 var tbh = -11; // fixme it needs to tool bar size!
26047 for (var i =0; i < this.toolbars.length;i++) {
26048 // fixme - ask toolbars for heights?
26049 tbh += this.toolbars[i].el.getHeight();
26050 //if (this.toolbars[i].footer) {
26051 // tbh += this.toolbars[i].footer.el.getHeight();
26059 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26060 ah -= 5; // knock a few pixes off for look..
26061 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26065 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26066 this.editorcore.onResize(ew,eh);
26071 * Toggles the editor between standard and source edit mode.
26072 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26074 toggleSourceEdit : function(sourceEditMode)
26076 this.editorcore.toggleSourceEdit(sourceEditMode);
26078 if(this.editorcore.sourceEditMode){
26079 Roo.log('editor - showing textarea');
26082 // Roo.log(this.syncValue());
26084 this.inputEl().removeClass(['hide', 'x-hidden']);
26085 this.inputEl().dom.removeAttribute('tabIndex');
26086 this.inputEl().focus();
26088 Roo.log('editor - hiding textarea');
26090 // Roo.log(this.pushValue());
26093 this.inputEl().addClass(['hide', 'x-hidden']);
26094 this.inputEl().dom.setAttribute('tabIndex', -1);
26095 //this.deferFocus();
26098 if(this.resizable){
26099 this.setSize(this.wrap.getSize());
26102 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26105 // private (for BoxComponent)
26106 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26108 // private (for BoxComponent)
26109 getResizeEl : function(){
26113 // private (for BoxComponent)
26114 getPositionEl : function(){
26119 initEvents : function(){
26120 this.originalValue = this.getValue();
26124 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26127 // markInvalid : Roo.emptyFn,
26129 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26132 // clearInvalid : Roo.emptyFn,
26134 setValue : function(v){
26135 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26136 this.editorcore.pushValue();
26141 deferFocus : function(){
26142 this.focus.defer(10, this);
26146 focus : function(){
26147 this.editorcore.focus();
26153 onDestroy : function(){
26159 for (var i =0; i < this.toolbars.length;i++) {
26160 // fixme - ask toolbars for heights?
26161 this.toolbars[i].onDestroy();
26164 this.wrap.dom.innerHTML = '';
26165 this.wrap.remove();
26170 onFirstFocus : function(){
26171 //Roo.log("onFirstFocus");
26172 this.editorcore.onFirstFocus();
26173 for (var i =0; i < this.toolbars.length;i++) {
26174 this.toolbars[i].onFirstFocus();
26180 syncValue : function()
26182 this.editorcore.syncValue();
26185 pushValue : function()
26187 this.editorcore.pushValue();
26191 // hide stuff that is not compatible
26205 * @event specialkey
26209 * @cfg {String} fieldClass @hide
26212 * @cfg {String} focusClass @hide
26215 * @cfg {String} autoCreate @hide
26218 * @cfg {String} inputType @hide
26222 * @cfg {String} invalidText @hide
26225 * @cfg {String} msgFx @hide
26228 * @cfg {String} validateOnBlur @hide
26237 Roo.namespace('Roo.bootstrap.htmleditor');
26239 * @class Roo.bootstrap.HtmlEditorToolbar1
26245 new Roo.bootstrap.HtmlEditor({
26248 new Roo.bootstrap.HtmlEditorToolbar1({
26249 disable : { fonts: 1 , format: 1, ..., ... , ...],
26255 * @cfg {Object} disable List of elements to disable..
26256 * @cfg {Array} btns List of additional buttons.
26260 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26263 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26266 Roo.apply(this, config);
26268 // default disabled, based on 'good practice'..
26269 this.disable = this.disable || {};
26270 Roo.applyIf(this.disable, {
26273 specialElements : true
26275 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26277 this.editor = config.editor;
26278 this.editorcore = config.editor.editorcore;
26280 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26282 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26283 // dont call parent... till later.
26285 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26290 editorcore : false,
26295 "h1","h2","h3","h4","h5","h6",
26297 "abbr", "acronym", "address", "cite", "samp", "var",
26301 onRender : function(ct, position)
26303 // Roo.log("Call onRender: " + this.xtype);
26305 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26307 this.el.dom.style.marginBottom = '0';
26309 var editorcore = this.editorcore;
26310 var editor= this.editor;
26313 var btn = function(id,cmd , toggle, handler, html){
26315 var event = toggle ? 'toggle' : 'click';
26320 xns: Roo.bootstrap,
26324 enableToggle:toggle !== false,
26326 pressed : toggle ? false : null,
26329 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26330 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26336 // var cb_box = function...
26341 xns: Roo.bootstrap,
26346 xns: Roo.bootstrap,
26350 Roo.each(this.formats, function(f) {
26351 style.menu.items.push({
26353 xns: Roo.bootstrap,
26354 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26359 editorcore.insertTag(this.tagname);
26366 children.push(style);
26368 btn('bold',false,true);
26369 btn('italic',false,true);
26370 btn('align-left', 'justifyleft',true);
26371 btn('align-center', 'justifycenter',true);
26372 btn('align-right' , 'justifyright',true);
26373 btn('link', false, false, function(btn) {
26374 //Roo.log("create link?");
26375 var url = prompt(this.createLinkText, this.defaultLinkValue);
26376 if(url && url != 'http:/'+'/'){
26377 this.editorcore.relayCmd('createlink', url);
26380 btn('list','insertunorderedlist',true);
26381 btn('pencil', false,true, function(btn){
26383 this.toggleSourceEdit(btn.pressed);
26386 if (this.editor.btns.length > 0) {
26387 for (var i = 0; i<this.editor.btns.length; i++) {
26388 children.push(this.editor.btns[i]);
26396 xns: Roo.bootstrap,
26401 xns: Roo.bootstrap,
26406 cog.menu.items.push({
26408 xns: Roo.bootstrap,
26409 html : Clean styles,
26414 editorcore.insertTag(this.tagname);
26423 this.xtype = 'NavSimplebar';
26425 for(var i=0;i< children.length;i++) {
26427 this.buttons.add(this.addxtypeChild(children[i]));
26431 editor.on('editorevent', this.updateToolbar, this);
26433 onBtnClick : function(id)
26435 this.editorcore.relayCmd(id);
26436 this.editorcore.focus();
26440 * Protected method that will not generally be called directly. It triggers
26441 * a toolbar update by reading the markup state of the current selection in the editor.
26443 updateToolbar: function(){
26445 if(!this.editorcore.activated){
26446 this.editor.onFirstFocus(); // is this neeed?
26450 var btns = this.buttons;
26451 var doc = this.editorcore.doc;
26452 btns.get('bold').setActive(doc.queryCommandState('bold'));
26453 btns.get('italic').setActive(doc.queryCommandState('italic'));
26454 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26456 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26457 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26458 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26460 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26461 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26464 var ans = this.editorcore.getAllAncestors();
26465 if (this.formatCombo) {
26468 var store = this.formatCombo.store;
26469 this.formatCombo.setValue("");
26470 for (var i =0; i < ans.length;i++) {
26471 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26473 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26481 // hides menus... - so this cant be on a menu...
26482 Roo.bootstrap.MenuMgr.hideAll();
26484 Roo.bootstrap.MenuMgr.hideAll();
26485 //this.editorsyncValue();
26487 onFirstFocus: function() {
26488 this.buttons.each(function(item){
26492 toggleSourceEdit : function(sourceEditMode){
26495 if(sourceEditMode){
26496 Roo.log("disabling buttons");
26497 this.buttons.each( function(item){
26498 if(item.cmd != 'pencil'){
26504 Roo.log("enabling buttons");
26505 if(this.editorcore.initialized){
26506 this.buttons.each( function(item){
26512 Roo.log("calling toggole on editor");
26513 // tell the editor that it's been pressed..
26514 this.editor.toggleSourceEdit(sourceEditMode);
26528 * @class Roo.bootstrap.Markdown
26529 * @extends Roo.bootstrap.TextArea
26530 * Bootstrap Showdown editable area
26531 * @cfg {string} content
26534 * Create a new Showdown
26537 Roo.bootstrap.Markdown = function(config){
26538 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26542 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26546 initEvents : function()
26549 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26550 this.markdownEl = this.el.createChild({
26551 cls : 'roo-markdown-area'
26553 this.inputEl().addClass('d-none');
26554 if (this.getValue() == '') {
26555 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26558 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26560 this.markdownEl.on('click', this.toggleTextEdit, this);
26561 this.on('blur', this.toggleTextEdit, this);
26562 this.on('specialkey', this.resizeTextArea, this);
26565 toggleTextEdit : function()
26567 var sh = this.markdownEl.getHeight();
26568 this.inputEl().addClass('d-none');
26569 this.markdownEl.addClass('d-none');
26570 if (!this.editing) {
26572 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26573 this.inputEl().removeClass('d-none');
26574 this.inputEl().focus();
26575 this.editing = true;
26578 // show showdown...
26579 this.updateMarkdown();
26580 this.markdownEl.removeClass('d-none');
26581 this.editing = false;
26584 updateMarkdown : function()
26586 if (this.getValue() == '') {
26587 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26591 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26594 resizeTextArea: function () {
26597 Roo.log([sh, this.getValue().split("\n").length * 30]);
26598 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26600 setValue : function(val)
26602 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26603 if (!this.editing) {
26604 this.updateMarkdown();
26610 if (!this.editing) {
26611 this.toggleTextEdit();
26619 * @class Roo.bootstrap.Table.AbstractSelectionModel
26620 * @extends Roo.util.Observable
26621 * Abstract base class for grid SelectionModels. It provides the interface that should be
26622 * implemented by descendant classes. This class should not be directly instantiated.
26625 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26626 this.locked = false;
26627 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26631 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26632 /** @ignore Called by the grid automatically. Do not call directly. */
26633 init : function(grid){
26639 * Locks the selections.
26642 this.locked = true;
26646 * Unlocks the selections.
26648 unlock : function(){
26649 this.locked = false;
26653 * Returns true if the selections are locked.
26654 * @return {Boolean}
26656 isLocked : function(){
26657 return this.locked;
26661 initEvents : function ()
26667 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26668 * @class Roo.bootstrap.Table.RowSelectionModel
26669 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26670 * It supports multiple selections and keyboard selection/navigation.
26672 * @param {Object} config
26675 Roo.bootstrap.Table.RowSelectionModel = function(config){
26676 Roo.apply(this, config);
26677 this.selections = new Roo.util.MixedCollection(false, function(o){
26682 this.lastActive = false;
26686 * @event selectionchange
26687 * Fires when the selection changes
26688 * @param {SelectionModel} this
26690 "selectionchange" : true,
26692 * @event afterselectionchange
26693 * Fires after the selection changes (eg. by key press or clicking)
26694 * @param {SelectionModel} this
26696 "afterselectionchange" : true,
26698 * @event beforerowselect
26699 * Fires when a row is selected being selected, return false to cancel.
26700 * @param {SelectionModel} this
26701 * @param {Number} rowIndex The selected index
26702 * @param {Boolean} keepExisting False if other selections will be cleared
26704 "beforerowselect" : true,
26707 * Fires when a row is selected.
26708 * @param {SelectionModel} this
26709 * @param {Number} rowIndex The selected index
26710 * @param {Roo.data.Record} r The record
26712 "rowselect" : true,
26714 * @event rowdeselect
26715 * Fires when a row is deselected.
26716 * @param {SelectionModel} this
26717 * @param {Number} rowIndex The selected index
26719 "rowdeselect" : true
26721 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26722 this.locked = false;
26725 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26727 * @cfg {Boolean} singleSelect
26728 * True to allow selection of only one row at a time (defaults to false)
26730 singleSelect : false,
26733 initEvents : function()
26736 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26737 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26738 //}else{ // allow click to work like normal
26739 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26741 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26742 this.grid.on("rowclick", this.handleMouseDown, this);
26744 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26745 "up" : function(e){
26747 this.selectPrevious(e.shiftKey);
26748 }else if(this.last !== false && this.lastActive !== false){
26749 var last = this.last;
26750 this.selectRange(this.last, this.lastActive-1);
26751 this.grid.getView().focusRow(this.lastActive);
26752 if(last !== false){
26756 this.selectFirstRow();
26758 this.fireEvent("afterselectionchange", this);
26760 "down" : function(e){
26762 this.selectNext(e.shiftKey);
26763 }else if(this.last !== false && this.lastActive !== false){
26764 var last = this.last;
26765 this.selectRange(this.last, this.lastActive+1);
26766 this.grid.getView().focusRow(this.lastActive);
26767 if(last !== false){
26771 this.selectFirstRow();
26773 this.fireEvent("afterselectionchange", this);
26777 this.grid.store.on('load', function(){
26778 this.selections.clear();
26781 var view = this.grid.view;
26782 view.on("refresh", this.onRefresh, this);
26783 view.on("rowupdated", this.onRowUpdated, this);
26784 view.on("rowremoved", this.onRemove, this);
26789 onRefresh : function()
26791 var ds = this.grid.store, i, v = this.grid.view;
26792 var s = this.selections;
26793 s.each(function(r){
26794 if((i = ds.indexOfId(r.id)) != -1){
26803 onRemove : function(v, index, r){
26804 this.selections.remove(r);
26808 onRowUpdated : function(v, index, r){
26809 if(this.isSelected(r)){
26810 v.onRowSelect(index);
26816 * @param {Array} records The records to select
26817 * @param {Boolean} keepExisting (optional) True to keep existing selections
26819 selectRecords : function(records, keepExisting)
26822 this.clearSelections();
26824 var ds = this.grid.store;
26825 for(var i = 0, len = records.length; i < len; i++){
26826 this.selectRow(ds.indexOf(records[i]), true);
26831 * Gets the number of selected rows.
26834 getCount : function(){
26835 return this.selections.length;
26839 * Selects the first row in the grid.
26841 selectFirstRow : function(){
26846 * Select the last row.
26847 * @param {Boolean} keepExisting (optional) True to keep existing selections
26849 selectLastRow : function(keepExisting){
26850 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26851 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26855 * Selects the row immediately following the last selected row.
26856 * @param {Boolean} keepExisting (optional) True to keep existing selections
26858 selectNext : function(keepExisting)
26860 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26861 this.selectRow(this.last+1, keepExisting);
26862 this.grid.getView().focusRow(this.last);
26867 * Selects the row that precedes the last selected row.
26868 * @param {Boolean} keepExisting (optional) True to keep existing selections
26870 selectPrevious : function(keepExisting){
26872 this.selectRow(this.last-1, keepExisting);
26873 this.grid.getView().focusRow(this.last);
26878 * Returns the selected records
26879 * @return {Array} Array of selected records
26881 getSelections : function(){
26882 return [].concat(this.selections.items);
26886 * Returns the first selected record.
26889 getSelected : function(){
26890 return this.selections.itemAt(0);
26895 * Clears all selections.
26897 clearSelections : function(fast)
26903 var ds = this.grid.store;
26904 var s = this.selections;
26905 s.each(function(r){
26906 this.deselectRow(ds.indexOfId(r.id));
26910 this.selections.clear();
26917 * Selects all rows.
26919 selectAll : function(){
26923 this.selections.clear();
26924 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26925 this.selectRow(i, true);
26930 * Returns True if there is a selection.
26931 * @return {Boolean}
26933 hasSelection : function(){
26934 return this.selections.length > 0;
26938 * Returns True if the specified row is selected.
26939 * @param {Number/Record} record The record or index of the record to check
26940 * @return {Boolean}
26942 isSelected : function(index){
26943 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26944 return (r && this.selections.key(r.id) ? true : false);
26948 * Returns True if the specified record id is selected.
26949 * @param {String} id The id of record to check
26950 * @return {Boolean}
26952 isIdSelected : function(id){
26953 return (this.selections.key(id) ? true : false);
26958 handleMouseDBClick : function(e, t){
26962 handleMouseDown : function(e, t)
26964 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26965 if(this.isLocked() || rowIndex < 0 ){
26968 if(e.shiftKey && this.last !== false){
26969 var last = this.last;
26970 this.selectRange(last, rowIndex, e.ctrlKey);
26971 this.last = last; // reset the last
26975 var isSelected = this.isSelected(rowIndex);
26976 //Roo.log("select row:" + rowIndex);
26978 this.deselectRow(rowIndex);
26980 this.selectRow(rowIndex, true);
26984 if(e.button !== 0 && isSelected){
26985 alert('rowIndex 2: ' + rowIndex);
26986 view.focusRow(rowIndex);
26987 }else if(e.ctrlKey && isSelected){
26988 this.deselectRow(rowIndex);
26989 }else if(!isSelected){
26990 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26991 view.focusRow(rowIndex);
26995 this.fireEvent("afterselectionchange", this);
26998 handleDragableRowClick : function(grid, rowIndex, e)
27000 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27001 this.selectRow(rowIndex, false);
27002 grid.view.focusRow(rowIndex);
27003 this.fireEvent("afterselectionchange", this);
27008 * Selects multiple rows.
27009 * @param {Array} rows Array of the indexes of the row to select
27010 * @param {Boolean} keepExisting (optional) True to keep existing selections
27012 selectRows : function(rows, keepExisting){
27014 this.clearSelections();
27016 for(var i = 0, len = rows.length; i < len; i++){
27017 this.selectRow(rows[i], true);
27022 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27023 * @param {Number} startRow The index of the first row in the range
27024 * @param {Number} endRow The index of the last row in the range
27025 * @param {Boolean} keepExisting (optional) True to retain existing selections
27027 selectRange : function(startRow, endRow, keepExisting){
27032 this.clearSelections();
27034 if(startRow <= endRow){
27035 for(var i = startRow; i <= endRow; i++){
27036 this.selectRow(i, true);
27039 for(var i = startRow; i >= endRow; i--){
27040 this.selectRow(i, true);
27046 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27047 * @param {Number} startRow The index of the first row in the range
27048 * @param {Number} endRow The index of the last row in the range
27050 deselectRange : function(startRow, endRow, preventViewNotify){
27054 for(var i = startRow; i <= endRow; i++){
27055 this.deselectRow(i, preventViewNotify);
27061 * @param {Number} row The index of the row to select
27062 * @param {Boolean} keepExisting (optional) True to keep existing selections
27064 selectRow : function(index, keepExisting, preventViewNotify)
27066 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27069 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27070 if(!keepExisting || this.singleSelect){
27071 this.clearSelections();
27074 var r = this.grid.store.getAt(index);
27075 //console.log('selectRow - record id :' + r.id);
27077 this.selections.add(r);
27078 this.last = this.lastActive = index;
27079 if(!preventViewNotify){
27080 var proxy = new Roo.Element(
27081 this.grid.getRowDom(index)
27083 proxy.addClass('bg-info info');
27085 this.fireEvent("rowselect", this, index, r);
27086 this.fireEvent("selectionchange", this);
27092 * @param {Number} row The index of the row to deselect
27094 deselectRow : function(index, preventViewNotify)
27099 if(this.last == index){
27102 if(this.lastActive == index){
27103 this.lastActive = false;
27106 var r = this.grid.store.getAt(index);
27111 this.selections.remove(r);
27112 //.console.log('deselectRow - record id :' + r.id);
27113 if(!preventViewNotify){
27115 var proxy = new Roo.Element(
27116 this.grid.getRowDom(index)
27118 proxy.removeClass('bg-info info');
27120 this.fireEvent("rowdeselect", this, index);
27121 this.fireEvent("selectionchange", this);
27125 restoreLast : function(){
27127 this.last = this._last;
27132 acceptsNav : function(row, col, cm){
27133 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27137 onEditorKey : function(field, e){
27138 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27143 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27145 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27147 }else if(k == e.ENTER && !e.ctrlKey){
27151 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27153 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27155 }else if(k == e.ESC){
27159 g.startEditing(newCell[0], newCell[1]);
27165 * Ext JS Library 1.1.1
27166 * Copyright(c) 2006-2007, Ext JS, LLC.
27168 * Originally Released Under LGPL - original licence link has changed is not relivant.
27171 * <script type="text/javascript">
27175 * @class Roo.bootstrap.PagingToolbar
27176 * @extends Roo.bootstrap.NavSimplebar
27177 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27179 * Create a new PagingToolbar
27180 * @param {Object} config The config object
27181 * @param {Roo.data.Store} store
27183 Roo.bootstrap.PagingToolbar = function(config)
27185 // old args format still supported... - xtype is prefered..
27186 // created from xtype...
27188 this.ds = config.dataSource;
27190 if (config.store && !this.ds) {
27191 this.store= Roo.factory(config.store, Roo.data);
27192 this.ds = this.store;
27193 this.ds.xmodule = this.xmodule || false;
27196 this.toolbarItems = [];
27197 if (config.items) {
27198 this.toolbarItems = config.items;
27201 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27206 this.bind(this.ds);
27209 if (Roo.bootstrap.version == 4) {
27210 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27212 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27217 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27219 * @cfg {Roo.data.Store} dataSource
27220 * The underlying data store providing the paged data
27223 * @cfg {String/HTMLElement/Element} container
27224 * container The id or element that will contain the toolbar
27227 * @cfg {Boolean} displayInfo
27228 * True to display the displayMsg (defaults to false)
27231 * @cfg {Number} pageSize
27232 * The number of records to display per page (defaults to 20)
27236 * @cfg {String} displayMsg
27237 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27239 displayMsg : 'Displaying {0} - {1} of {2}',
27241 * @cfg {String} emptyMsg
27242 * The message to display when no records are found (defaults to "No data to display")
27244 emptyMsg : 'No data to display',
27246 * Customizable piece of the default paging text (defaults to "Page")
27249 beforePageText : "Page",
27251 * Customizable piece of the default paging text (defaults to "of %0")
27254 afterPageText : "of {0}",
27256 * Customizable piece of the default paging text (defaults to "First Page")
27259 firstText : "First Page",
27261 * Customizable piece of the default paging text (defaults to "Previous Page")
27264 prevText : "Previous Page",
27266 * Customizable piece of the default paging text (defaults to "Next Page")
27269 nextText : "Next Page",
27271 * Customizable piece of the default paging text (defaults to "Last Page")
27274 lastText : "Last Page",
27276 * Customizable piece of the default paging text (defaults to "Refresh")
27279 refreshText : "Refresh",
27283 onRender : function(ct, position)
27285 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27286 this.navgroup.parentId = this.id;
27287 this.navgroup.onRender(this.el, null);
27288 // add the buttons to the navgroup
27290 if(this.displayInfo){
27291 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27292 this.displayEl = this.el.select('.x-paging-info', true).first();
27293 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27294 // this.displayEl = navel.el.select('span',true).first();
27300 Roo.each(_this.buttons, function(e){ // this might need to use render????
27301 Roo.factory(e).render(_this.el);
27305 Roo.each(_this.toolbarItems, function(e) {
27306 _this.navgroup.addItem(e);
27310 this.first = this.navgroup.addItem({
27311 tooltip: this.firstText,
27312 cls: "prev btn-outline-secondary",
27313 html : ' <i class="fa fa-step-backward"></i>',
27315 preventDefault: true,
27316 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27319 this.prev = this.navgroup.addItem({
27320 tooltip: this.prevText,
27321 cls: "prev btn-outline-secondary",
27322 html : ' <i class="fa fa-backward"></i>',
27324 preventDefault: true,
27325 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27327 //this.addSeparator();
27330 var field = this.navgroup.addItem( {
27332 cls : 'x-paging-position btn-outline-secondary',
27334 html : this.beforePageText +
27335 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27336 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27339 this.field = field.el.select('input', true).first();
27340 this.field.on("keydown", this.onPagingKeydown, this);
27341 this.field.on("focus", function(){this.dom.select();});
27344 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27345 //this.field.setHeight(18);
27346 //this.addSeparator();
27347 this.next = this.navgroup.addItem({
27348 tooltip: this.nextText,
27349 cls: "next btn-outline-secondary",
27350 html : ' <i class="fa fa-forward"></i>',
27352 preventDefault: true,
27353 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27355 this.last = this.navgroup.addItem({
27356 tooltip: this.lastText,
27357 html : ' <i class="fa fa-step-forward"></i>',
27358 cls: "next btn-outline-secondary",
27360 preventDefault: true,
27361 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27363 //this.addSeparator();
27364 this.loading = this.navgroup.addItem({
27365 tooltip: this.refreshText,
27366 cls: "btn-outline-secondary",
27367 html : ' <i class="fa fa-refresh"></i>',
27368 preventDefault: true,
27369 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27375 updateInfo : function(){
27376 if(this.displayEl){
27377 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27378 var msg = count == 0 ?
27382 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27384 this.displayEl.update(msg);
27389 onLoad : function(ds, r, o)
27391 this.cursor = o.params && o.params.start ? o.params.start : 0;
27393 var d = this.getPageData(),
27398 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27399 this.field.dom.value = ap;
27400 this.first.setDisabled(ap == 1);
27401 this.prev.setDisabled(ap == 1);
27402 this.next.setDisabled(ap == ps);
27403 this.last.setDisabled(ap == ps);
27404 this.loading.enable();
27409 getPageData : function(){
27410 var total = this.ds.getTotalCount();
27413 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27414 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27419 onLoadError : function(){
27420 this.loading.enable();
27424 onPagingKeydown : function(e){
27425 var k = e.getKey();
27426 var d = this.getPageData();
27428 var v = this.field.dom.value, pageNum;
27429 if(!v || isNaN(pageNum = parseInt(v, 10))){
27430 this.field.dom.value = d.activePage;
27433 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27434 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27437 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))
27439 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27440 this.field.dom.value = pageNum;
27441 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27444 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27446 var v = this.field.dom.value, pageNum;
27447 var increment = (e.shiftKey) ? 10 : 1;
27448 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27451 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27452 this.field.dom.value = d.activePage;
27455 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27457 this.field.dom.value = parseInt(v, 10) + increment;
27458 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27459 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27466 beforeLoad : function(){
27468 this.loading.disable();
27473 onClick : function(which){
27482 ds.load({params:{start: 0, limit: this.pageSize}});
27485 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27488 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27491 var total = ds.getTotalCount();
27492 var extra = total % this.pageSize;
27493 var lastStart = extra ? (total - extra) : total-this.pageSize;
27494 ds.load({params:{start: lastStart, limit: this.pageSize}});
27497 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27503 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27504 * @param {Roo.data.Store} store The data store to unbind
27506 unbind : function(ds){
27507 ds.un("beforeload", this.beforeLoad, this);
27508 ds.un("load", this.onLoad, this);
27509 ds.un("loadexception", this.onLoadError, this);
27510 ds.un("remove", this.updateInfo, this);
27511 ds.un("add", this.updateInfo, this);
27512 this.ds = undefined;
27516 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27517 * @param {Roo.data.Store} store The data store to bind
27519 bind : function(ds){
27520 ds.on("beforeload", this.beforeLoad, this);
27521 ds.on("load", this.onLoad, this);
27522 ds.on("loadexception", this.onLoadError, this);
27523 ds.on("remove", this.updateInfo, this);
27524 ds.on("add", this.updateInfo, this);
27535 * @class Roo.bootstrap.MessageBar
27536 * @extends Roo.bootstrap.Component
27537 * Bootstrap MessageBar class
27538 * @cfg {String} html contents of the MessageBar
27539 * @cfg {String} weight (info | success | warning | danger) default info
27540 * @cfg {String} beforeClass insert the bar before the given class
27541 * @cfg {Boolean} closable (true | false) default false
27542 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27545 * Create a new Element
27546 * @param {Object} config The config object
27549 Roo.bootstrap.MessageBar = function(config){
27550 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27553 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27559 beforeClass: 'bootstrap-sticky-wrap',
27561 getAutoCreate : function(){
27565 cls: 'alert alert-dismissable alert-' + this.weight,
27570 html: this.html || ''
27576 cfg.cls += ' alert-messages-fixed';
27590 onRender : function(ct, position)
27592 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27595 var cfg = Roo.apply({}, this.getAutoCreate());
27599 cfg.cls += ' ' + this.cls;
27602 cfg.style = this.style;
27604 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27606 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27609 this.el.select('>button.close').on('click', this.hide, this);
27615 if (!this.rendered) {
27621 this.fireEvent('show', this);
27627 if (!this.rendered) {
27633 this.fireEvent('hide', this);
27636 update : function()
27638 // var e = this.el.dom.firstChild;
27640 // if(this.closable){
27641 // e = e.nextSibling;
27644 // e.data = this.html || '';
27646 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27662 * @class Roo.bootstrap.Graph
27663 * @extends Roo.bootstrap.Component
27664 * Bootstrap Graph class
27668 @cfg {String} graphtype bar | vbar | pie
27669 @cfg {number} g_x coodinator | centre x (pie)
27670 @cfg {number} g_y coodinator | centre y (pie)
27671 @cfg {number} g_r radius (pie)
27672 @cfg {number} g_height height of the chart (respected by all elements in the set)
27673 @cfg {number} g_width width of the chart (respected by all elements in the set)
27674 @cfg {Object} title The title of the chart
27677 -opts (object) options for the chart
27679 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27680 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27682 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.
27683 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27685 o stretch (boolean)
27687 -opts (object) options for the pie
27690 o startAngle (number)
27691 o endAngle (number)
27695 * Create a new Input
27696 * @param {Object} config The config object
27699 Roo.bootstrap.Graph = function(config){
27700 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27706 * The img click event for the img.
27707 * @param {Roo.EventObject} e
27713 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27724 //g_colors: this.colors,
27731 getAutoCreate : function(){
27742 onRender : function(ct,position){
27745 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27747 if (typeof(Raphael) == 'undefined') {
27748 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27752 this.raphael = Raphael(this.el.dom);
27754 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27755 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27756 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27757 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27759 r.text(160, 10, "Single Series Chart").attr(txtattr);
27760 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27761 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27762 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27764 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27765 r.barchart(330, 10, 300, 220, data1);
27766 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27767 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27770 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27771 // r.barchart(30, 30, 560, 250, xdata, {
27772 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27773 // axis : "0 0 1 1",
27774 // axisxlabels : xdata
27775 // //yvalues : cols,
27778 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27780 // this.load(null,xdata,{
27781 // axis : "0 0 1 1",
27782 // axisxlabels : xdata
27787 load : function(graphtype,xdata,opts)
27789 this.raphael.clear();
27791 graphtype = this.graphtype;
27796 var r = this.raphael,
27797 fin = function () {
27798 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27800 fout = function () {
27801 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27803 pfin = function() {
27804 this.sector.stop();
27805 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27808 this.label[0].stop();
27809 this.label[0].attr({ r: 7.5 });
27810 this.label[1].attr({ "font-weight": 800 });
27813 pfout = function() {
27814 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27817 this.label[0].animate({ r: 5 }, 500, "bounce");
27818 this.label[1].attr({ "font-weight": 400 });
27824 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27827 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27830 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27831 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27833 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27840 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27845 setTitle: function(o)
27850 initEvents: function() {
27853 this.el.on('click', this.onClick, this);
27857 onClick : function(e)
27859 Roo.log('img onclick');
27860 this.fireEvent('click', this, e);
27872 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27875 * @class Roo.bootstrap.dash.NumberBox
27876 * @extends Roo.bootstrap.Component
27877 * Bootstrap NumberBox class
27878 * @cfg {String} headline Box headline
27879 * @cfg {String} content Box content
27880 * @cfg {String} icon Box icon
27881 * @cfg {String} footer Footer text
27882 * @cfg {String} fhref Footer href
27885 * Create a new NumberBox
27886 * @param {Object} config The config object
27890 Roo.bootstrap.dash.NumberBox = function(config){
27891 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27895 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27904 getAutoCreate : function(){
27908 cls : 'small-box ',
27916 cls : 'roo-headline',
27917 html : this.headline
27921 cls : 'roo-content',
27922 html : this.content
27936 cls : 'ion ' + this.icon
27945 cls : 'small-box-footer',
27946 href : this.fhref || '#',
27950 cfg.cn.push(footer);
27957 onRender : function(ct,position){
27958 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27965 setHeadline: function (value)
27967 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27970 setFooter: function (value, href)
27972 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27975 this.el.select('a.small-box-footer',true).first().attr('href', href);
27980 setContent: function (value)
27982 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27985 initEvents: function()
27999 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28002 * @class Roo.bootstrap.dash.TabBox
28003 * @extends Roo.bootstrap.Component
28004 * Bootstrap TabBox class
28005 * @cfg {String} title Title of the TabBox
28006 * @cfg {String} icon Icon of the TabBox
28007 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28008 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28011 * Create a new TabBox
28012 * @param {Object} config The config object
28016 Roo.bootstrap.dash.TabBox = function(config){
28017 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28022 * When a pane is added
28023 * @param {Roo.bootstrap.dash.TabPane} pane
28027 * @event activatepane
28028 * When a pane is activated
28029 * @param {Roo.bootstrap.dash.TabPane} pane
28031 "activatepane" : true
28039 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28044 tabScrollable : false,
28046 getChildContainer : function()
28048 return this.el.select('.tab-content', true).first();
28051 getAutoCreate : function(){
28055 cls: 'pull-left header',
28063 cls: 'fa ' + this.icon
28069 cls: 'nav nav-tabs pull-right',
28075 if(this.tabScrollable){
28082 cls: 'nav nav-tabs pull-right',
28093 cls: 'nav-tabs-custom',
28098 cls: 'tab-content no-padding',
28106 initEvents : function()
28108 //Roo.log('add add pane handler');
28109 this.on('addpane', this.onAddPane, this);
28112 * Updates the box title
28113 * @param {String} html to set the title to.
28115 setTitle : function(value)
28117 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28119 onAddPane : function(pane)
28121 this.panes.push(pane);
28122 //Roo.log('addpane');
28124 // tabs are rendere left to right..
28125 if(!this.showtabs){
28129 var ctr = this.el.select('.nav-tabs', true).first();
28132 var existing = ctr.select('.nav-tab',true);
28133 var qty = existing.getCount();;
28136 var tab = ctr.createChild({
28138 cls : 'nav-tab' + (qty ? '' : ' active'),
28146 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28149 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28151 pane.el.addClass('active');
28156 onTabClick : function(ev,un,ob,pane)
28158 //Roo.log('tab - prev default');
28159 ev.preventDefault();
28162 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28163 pane.tab.addClass('active');
28164 //Roo.log(pane.title);
28165 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28166 // technically we should have a deactivate event.. but maybe add later.
28167 // and it should not de-activate the selected tab...
28168 this.fireEvent('activatepane', pane);
28169 pane.el.addClass('active');
28170 pane.fireEvent('activate');
28175 getActivePane : function()
28178 Roo.each(this.panes, function(p) {
28179 if(p.el.hasClass('active')){
28200 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28202 * @class Roo.bootstrap.TabPane
28203 * @extends Roo.bootstrap.Component
28204 * Bootstrap TabPane class
28205 * @cfg {Boolean} active (false | true) Default false
28206 * @cfg {String} title title of panel
28210 * Create a new TabPane
28211 * @param {Object} config The config object
28214 Roo.bootstrap.dash.TabPane = function(config){
28215 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28221 * When a pane is activated
28222 * @param {Roo.bootstrap.dash.TabPane} pane
28229 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28234 // the tabBox that this is attached to.
28237 getAutoCreate : function()
28245 cfg.cls += ' active';
28250 initEvents : function()
28252 //Roo.log('trigger add pane handler');
28253 this.parent().fireEvent('addpane', this)
28257 * Updates the tab title
28258 * @param {String} html to set the title to.
28260 setTitle: function(str)
28266 this.tab.select('a', true).first().dom.innerHTML = str;
28283 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28286 * @class Roo.bootstrap.menu.Menu
28287 * @extends Roo.bootstrap.Component
28288 * Bootstrap Menu class - container for Menu
28289 * @cfg {String} html Text of the menu
28290 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28291 * @cfg {String} icon Font awesome icon
28292 * @cfg {String} pos Menu align to (top | bottom) default bottom
28296 * Create a new Menu
28297 * @param {Object} config The config object
28301 Roo.bootstrap.menu.Menu = function(config){
28302 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28306 * @event beforeshow
28307 * Fires before this menu is displayed
28308 * @param {Roo.bootstrap.menu.Menu} this
28312 * @event beforehide
28313 * Fires before this menu is hidden
28314 * @param {Roo.bootstrap.menu.Menu} this
28319 * Fires after this menu is displayed
28320 * @param {Roo.bootstrap.menu.Menu} this
28325 * Fires after this menu is hidden
28326 * @param {Roo.bootstrap.menu.Menu} this
28331 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28332 * @param {Roo.bootstrap.menu.Menu} this
28333 * @param {Roo.EventObject} e
28340 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28344 weight : 'default',
28349 getChildContainer : function() {
28350 if(this.isSubMenu){
28354 return this.el.select('ul.dropdown-menu', true).first();
28357 getAutoCreate : function()
28362 cls : 'roo-menu-text',
28370 cls : 'fa ' + this.icon
28381 cls : 'dropdown-button btn btn-' + this.weight,
28386 cls : 'dropdown-toggle btn btn-' + this.weight,
28396 cls : 'dropdown-menu'
28402 if(this.pos == 'top'){
28403 cfg.cls += ' dropup';
28406 if(this.isSubMenu){
28409 cls : 'dropdown-menu'
28416 onRender : function(ct, position)
28418 this.isSubMenu = ct.hasClass('dropdown-submenu');
28420 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28423 initEvents : function()
28425 if(this.isSubMenu){
28429 this.hidden = true;
28431 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28432 this.triggerEl.on('click', this.onTriggerPress, this);
28434 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28435 this.buttonEl.on('click', this.onClick, this);
28441 if(this.isSubMenu){
28445 return this.el.select('ul.dropdown-menu', true).first();
28448 onClick : function(e)
28450 this.fireEvent("click", this, e);
28453 onTriggerPress : function(e)
28455 if (this.isVisible()) {
28462 isVisible : function(){
28463 return !this.hidden;
28468 this.fireEvent("beforeshow", this);
28470 this.hidden = false;
28471 this.el.addClass('open');
28473 Roo.get(document).on("mouseup", this.onMouseUp, this);
28475 this.fireEvent("show", this);
28482 this.fireEvent("beforehide", this);
28484 this.hidden = true;
28485 this.el.removeClass('open');
28487 Roo.get(document).un("mouseup", this.onMouseUp);
28489 this.fireEvent("hide", this);
28492 onMouseUp : function()
28506 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28509 * @class Roo.bootstrap.menu.Item
28510 * @extends Roo.bootstrap.Component
28511 * Bootstrap MenuItem class
28512 * @cfg {Boolean} submenu (true | false) default false
28513 * @cfg {String} html text of the item
28514 * @cfg {String} href the link
28515 * @cfg {Boolean} disable (true | false) default false
28516 * @cfg {Boolean} preventDefault (true | false) default true
28517 * @cfg {String} icon Font awesome icon
28518 * @cfg {String} pos Submenu align to (left | right) default right
28522 * Create a new Item
28523 * @param {Object} config The config object
28527 Roo.bootstrap.menu.Item = function(config){
28528 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28532 * Fires when the mouse is hovering over this menu
28533 * @param {Roo.bootstrap.menu.Item} this
28534 * @param {Roo.EventObject} e
28539 * Fires when the mouse exits this menu
28540 * @param {Roo.bootstrap.menu.Item} this
28541 * @param {Roo.EventObject} e
28547 * The raw click event for the entire grid.
28548 * @param {Roo.EventObject} e
28554 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28559 preventDefault: true,
28564 getAutoCreate : function()
28569 cls : 'roo-menu-item-text',
28577 cls : 'fa ' + this.icon
28586 href : this.href || '#',
28593 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28597 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28599 if(this.pos == 'left'){
28600 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28607 initEvents : function()
28609 this.el.on('mouseover', this.onMouseOver, this);
28610 this.el.on('mouseout', this.onMouseOut, this);
28612 this.el.select('a', true).first().on('click', this.onClick, this);
28616 onClick : function(e)
28618 if(this.preventDefault){
28619 e.preventDefault();
28622 this.fireEvent("click", this, e);
28625 onMouseOver : function(e)
28627 if(this.submenu && this.pos == 'left'){
28628 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28631 this.fireEvent("mouseover", this, e);
28634 onMouseOut : function(e)
28636 this.fireEvent("mouseout", this, e);
28648 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28651 * @class Roo.bootstrap.menu.Separator
28652 * @extends Roo.bootstrap.Component
28653 * Bootstrap Separator class
28656 * Create a new Separator
28657 * @param {Object} config The config object
28661 Roo.bootstrap.menu.Separator = function(config){
28662 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28665 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28667 getAutoCreate : function(){
28688 * @class Roo.bootstrap.Tooltip
28689 * Bootstrap Tooltip class
28690 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28691 * to determine which dom element triggers the tooltip.
28693 * It needs to add support for additional attributes like tooltip-position
28696 * Create a new Toolti
28697 * @param {Object} config The config object
28700 Roo.bootstrap.Tooltip = function(config){
28701 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28703 this.alignment = Roo.bootstrap.Tooltip.alignment;
28705 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28706 this.alignment = config.alignment;
28711 Roo.apply(Roo.bootstrap.Tooltip, {
28713 * @function init initialize tooltip monitoring.
28717 currentTip : false,
28718 currentRegion : false,
28724 Roo.get(document).on('mouseover', this.enter ,this);
28725 Roo.get(document).on('mouseout', this.leave, this);
28728 this.currentTip = new Roo.bootstrap.Tooltip();
28731 enter : function(ev)
28733 var dom = ev.getTarget();
28735 //Roo.log(['enter',dom]);
28736 var el = Roo.fly(dom);
28737 if (this.currentEl) {
28739 //Roo.log(this.currentEl);
28740 //Roo.log(this.currentEl.contains(dom));
28741 if (this.currentEl == el) {
28744 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28750 if (this.currentTip.el) {
28751 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28755 if(!el || el.dom == document){
28761 // you can not look for children, as if el is the body.. then everythign is the child..
28762 if (!el.attr('tooltip')) { //
28763 if (!el.select("[tooltip]").elements.length) {
28766 // is the mouse over this child...?
28767 bindEl = el.select("[tooltip]").first();
28768 var xy = ev.getXY();
28769 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28770 //Roo.log("not in region.");
28773 //Roo.log("child element over..");
28776 this.currentEl = bindEl;
28777 this.currentTip.bind(bindEl);
28778 this.currentRegion = Roo.lib.Region.getRegion(dom);
28779 this.currentTip.enter();
28782 leave : function(ev)
28784 var dom = ev.getTarget();
28785 //Roo.log(['leave',dom]);
28786 if (!this.currentEl) {
28791 if (dom != this.currentEl.dom) {
28794 var xy = ev.getXY();
28795 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28798 // only activate leave if mouse cursor is outside... bounding box..
28803 if (this.currentTip) {
28804 this.currentTip.leave();
28806 //Roo.log('clear currentEl');
28807 this.currentEl = false;
28812 'left' : ['r-l', [-2,0], 'right'],
28813 'right' : ['l-r', [2,0], 'left'],
28814 'bottom' : ['t-b', [0,2], 'top'],
28815 'top' : [ 'b-t', [0,-2], 'bottom']
28821 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28826 delay : null, // can be { show : 300 , hide: 500}
28830 hoverState : null, //???
28832 placement : 'bottom',
28836 getAutoCreate : function(){
28843 cls : 'tooltip-arrow arrow'
28846 cls : 'tooltip-inner'
28853 bind : function(el)
28858 initEvents : function()
28860 this.arrowEl = this.el.select('.arrow', true).first();
28861 this.innerEl = this.el.select('.tooltip-inner', true).first();
28864 enter : function () {
28866 if (this.timeout != null) {
28867 clearTimeout(this.timeout);
28870 this.hoverState = 'in';
28871 //Roo.log("enter - show");
28872 if (!this.delay || !this.delay.show) {
28877 this.timeout = setTimeout(function () {
28878 if (_t.hoverState == 'in') {
28881 }, this.delay.show);
28885 clearTimeout(this.timeout);
28887 this.hoverState = 'out';
28888 if (!this.delay || !this.delay.hide) {
28894 this.timeout = setTimeout(function () {
28895 //Roo.log("leave - timeout");
28897 if (_t.hoverState == 'out') {
28899 Roo.bootstrap.Tooltip.currentEl = false;
28904 show : function (msg)
28907 this.render(document.body);
28910 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28912 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28914 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28916 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28917 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28919 var placement = typeof this.placement == 'function' ?
28920 this.placement.call(this, this.el, on_el) :
28923 var autoToken = /\s?auto?\s?/i;
28924 var autoPlace = autoToken.test(placement);
28926 placement = placement.replace(autoToken, '') || 'top';
28930 //this.el.setXY([0,0]);
28932 //this.el.dom.style.display='block';
28934 //this.el.appendTo(on_el);
28936 var p = this.getPosition();
28937 var box = this.el.getBox();
28943 var align = this.alignment[placement];
28945 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28947 if(placement == 'top' || placement == 'bottom'){
28949 placement = 'right';
28952 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28953 placement = 'left';
28956 var scroll = Roo.select('body', true).first().getScroll();
28958 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28962 align = this.alignment[placement];
28964 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28968 this.el.alignTo(this.bindEl, align[0],align[1]);
28969 //var arrow = this.el.select('.arrow',true).first();
28970 //arrow.set(align[2],
28972 this.el.addClass(placement);
28973 this.el.addClass("bs-tooltip-"+ placement);
28975 this.el.addClass('in fade show');
28977 this.hoverState = null;
28979 if (this.el.hasClass('fade')) {
28994 //this.el.setXY([0,0]);
28995 this.el.removeClass(['show', 'in']);
29011 * @class Roo.bootstrap.LocationPicker
29012 * @extends Roo.bootstrap.Component
29013 * Bootstrap LocationPicker class
29014 * @cfg {Number} latitude Position when init default 0
29015 * @cfg {Number} longitude Position when init default 0
29016 * @cfg {Number} zoom default 15
29017 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29018 * @cfg {Boolean} mapTypeControl default false
29019 * @cfg {Boolean} disableDoubleClickZoom default false
29020 * @cfg {Boolean} scrollwheel default true
29021 * @cfg {Boolean} streetViewControl default false
29022 * @cfg {Number} radius default 0
29023 * @cfg {String} locationName
29024 * @cfg {Boolean} draggable default true
29025 * @cfg {Boolean} enableAutocomplete default false
29026 * @cfg {Boolean} enableReverseGeocode default true
29027 * @cfg {String} markerTitle
29030 * Create a new LocationPicker
29031 * @param {Object} config The config object
29035 Roo.bootstrap.LocationPicker = function(config){
29037 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29042 * Fires when the picker initialized.
29043 * @param {Roo.bootstrap.LocationPicker} this
29044 * @param {Google Location} location
29048 * @event positionchanged
29049 * Fires when the picker position changed.
29050 * @param {Roo.bootstrap.LocationPicker} this
29051 * @param {Google Location} location
29053 positionchanged : true,
29056 * Fires when the map resize.
29057 * @param {Roo.bootstrap.LocationPicker} this
29062 * Fires when the map show.
29063 * @param {Roo.bootstrap.LocationPicker} this
29068 * Fires when the map hide.
29069 * @param {Roo.bootstrap.LocationPicker} this
29074 * Fires when click the map.
29075 * @param {Roo.bootstrap.LocationPicker} this
29076 * @param {Map event} e
29080 * @event mapRightClick
29081 * Fires when right click the map.
29082 * @param {Roo.bootstrap.LocationPicker} this
29083 * @param {Map event} e
29085 mapRightClick : true,
29087 * @event markerClick
29088 * Fires when click the marker.
29089 * @param {Roo.bootstrap.LocationPicker} this
29090 * @param {Map event} e
29092 markerClick : true,
29094 * @event markerRightClick
29095 * Fires when right click the marker.
29096 * @param {Roo.bootstrap.LocationPicker} this
29097 * @param {Map event} e
29099 markerRightClick : true,
29101 * @event OverlayViewDraw
29102 * Fires when OverlayView Draw
29103 * @param {Roo.bootstrap.LocationPicker} this
29105 OverlayViewDraw : true,
29107 * @event OverlayViewOnAdd
29108 * Fires when OverlayView Draw
29109 * @param {Roo.bootstrap.LocationPicker} this
29111 OverlayViewOnAdd : true,
29113 * @event OverlayViewOnRemove
29114 * Fires when OverlayView Draw
29115 * @param {Roo.bootstrap.LocationPicker} this
29117 OverlayViewOnRemove : true,
29119 * @event OverlayViewShow
29120 * Fires when OverlayView Draw
29121 * @param {Roo.bootstrap.LocationPicker} this
29122 * @param {Pixel} cpx
29124 OverlayViewShow : true,
29126 * @event OverlayViewHide
29127 * Fires when OverlayView Draw
29128 * @param {Roo.bootstrap.LocationPicker} this
29130 OverlayViewHide : true,
29132 * @event loadexception
29133 * Fires when load google lib failed.
29134 * @param {Roo.bootstrap.LocationPicker} this
29136 loadexception : true
29141 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29143 gMapContext: false,
29149 mapTypeControl: false,
29150 disableDoubleClickZoom: false,
29152 streetViewControl: false,
29156 enableAutocomplete: false,
29157 enableReverseGeocode: true,
29160 getAutoCreate: function()
29165 cls: 'roo-location-picker'
29171 initEvents: function(ct, position)
29173 if(!this.el.getWidth() || this.isApplied()){
29177 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29182 initial: function()
29184 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29185 this.fireEvent('loadexception', this);
29189 if(!this.mapTypeId){
29190 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29193 this.gMapContext = this.GMapContext();
29195 this.initOverlayView();
29197 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29201 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29202 _this.setPosition(_this.gMapContext.marker.position);
29205 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29206 _this.fireEvent('mapClick', this, event);
29210 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29211 _this.fireEvent('mapRightClick', this, event);
29215 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29216 _this.fireEvent('markerClick', this, event);
29220 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29221 _this.fireEvent('markerRightClick', this, event);
29225 this.setPosition(this.gMapContext.location);
29227 this.fireEvent('initial', this, this.gMapContext.location);
29230 initOverlayView: function()
29234 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29238 _this.fireEvent('OverlayViewDraw', _this);
29243 _this.fireEvent('OverlayViewOnAdd', _this);
29246 onRemove: function()
29248 _this.fireEvent('OverlayViewOnRemove', _this);
29251 show: function(cpx)
29253 _this.fireEvent('OverlayViewShow', _this, cpx);
29258 _this.fireEvent('OverlayViewHide', _this);
29264 fromLatLngToContainerPixel: function(event)
29266 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29269 isApplied: function()
29271 return this.getGmapContext() == false ? false : true;
29274 getGmapContext: function()
29276 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29279 GMapContext: function()
29281 var position = new google.maps.LatLng(this.latitude, this.longitude);
29283 var _map = new google.maps.Map(this.el.dom, {
29286 mapTypeId: this.mapTypeId,
29287 mapTypeControl: this.mapTypeControl,
29288 disableDoubleClickZoom: this.disableDoubleClickZoom,
29289 scrollwheel: this.scrollwheel,
29290 streetViewControl: this.streetViewControl,
29291 locationName: this.locationName,
29292 draggable: this.draggable,
29293 enableAutocomplete: this.enableAutocomplete,
29294 enableReverseGeocode: this.enableReverseGeocode
29297 var _marker = new google.maps.Marker({
29298 position: position,
29300 title: this.markerTitle,
29301 draggable: this.draggable
29308 location: position,
29309 radius: this.radius,
29310 locationName: this.locationName,
29311 addressComponents: {
29312 formatted_address: null,
29313 addressLine1: null,
29314 addressLine2: null,
29316 streetNumber: null,
29320 stateOrProvince: null
29323 domContainer: this.el.dom,
29324 geodecoder: new google.maps.Geocoder()
29328 drawCircle: function(center, radius, options)
29330 if (this.gMapContext.circle != null) {
29331 this.gMapContext.circle.setMap(null);
29335 options = Roo.apply({}, options, {
29336 strokeColor: "#0000FF",
29337 strokeOpacity: .35,
29339 fillColor: "#0000FF",
29343 options.map = this.gMapContext.map;
29344 options.radius = radius;
29345 options.center = center;
29346 this.gMapContext.circle = new google.maps.Circle(options);
29347 return this.gMapContext.circle;
29353 setPosition: function(location)
29355 this.gMapContext.location = location;
29356 this.gMapContext.marker.setPosition(location);
29357 this.gMapContext.map.panTo(location);
29358 this.drawCircle(location, this.gMapContext.radius, {});
29362 if (this.gMapContext.settings.enableReverseGeocode) {
29363 this.gMapContext.geodecoder.geocode({
29364 latLng: this.gMapContext.location
29365 }, function(results, status) {
29367 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29368 _this.gMapContext.locationName = results[0].formatted_address;
29369 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29371 _this.fireEvent('positionchanged', this, location);
29378 this.fireEvent('positionchanged', this, location);
29383 google.maps.event.trigger(this.gMapContext.map, "resize");
29385 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29387 this.fireEvent('resize', this);
29390 setPositionByLatLng: function(latitude, longitude)
29392 this.setPosition(new google.maps.LatLng(latitude, longitude));
29395 getCurrentPosition: function()
29398 latitude: this.gMapContext.location.lat(),
29399 longitude: this.gMapContext.location.lng()
29403 getAddressName: function()
29405 return this.gMapContext.locationName;
29408 getAddressComponents: function()
29410 return this.gMapContext.addressComponents;
29413 address_component_from_google_geocode: function(address_components)
29417 for (var i = 0; i < address_components.length; i++) {
29418 var component = address_components[i];
29419 if (component.types.indexOf("postal_code") >= 0) {
29420 result.postalCode = component.short_name;
29421 } else if (component.types.indexOf("street_number") >= 0) {
29422 result.streetNumber = component.short_name;
29423 } else if (component.types.indexOf("route") >= 0) {
29424 result.streetName = component.short_name;
29425 } else if (component.types.indexOf("neighborhood") >= 0) {
29426 result.city = component.short_name;
29427 } else if (component.types.indexOf("locality") >= 0) {
29428 result.city = component.short_name;
29429 } else if (component.types.indexOf("sublocality") >= 0) {
29430 result.district = component.short_name;
29431 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29432 result.stateOrProvince = component.short_name;
29433 } else if (component.types.indexOf("country") >= 0) {
29434 result.country = component.short_name;
29438 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29439 result.addressLine2 = "";
29443 setZoomLevel: function(zoom)
29445 this.gMapContext.map.setZoom(zoom);
29458 this.fireEvent('show', this);
29469 this.fireEvent('hide', this);
29474 Roo.apply(Roo.bootstrap.LocationPicker, {
29476 OverlayView : function(map, options)
29478 options = options || {};
29485 * @class Roo.bootstrap.Alert
29486 * @extends Roo.bootstrap.Component
29487 * Bootstrap Alert class - shows an alert area box
29489 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29490 Enter a valid email address
29493 * @cfg {String} title The title of alert
29494 * @cfg {String} html The content of alert
29495 * @cfg {String} weight ( success | info | warning | danger )
29496 * @cfg {String} faicon font-awesomeicon
29499 * Create a new alert
29500 * @param {Object} config The config object
29504 Roo.bootstrap.Alert = function(config){
29505 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29509 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29516 getAutoCreate : function()
29525 cls : 'roo-alert-icon'
29530 cls : 'roo-alert-title',
29535 cls : 'roo-alert-text',
29542 cfg.cn[0].cls += ' fa ' + this.faicon;
29546 cfg.cls += ' alert-' + this.weight;
29552 initEvents: function()
29554 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29557 setTitle : function(str)
29559 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29562 setText : function(str)
29564 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29567 setWeight : function(weight)
29570 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29573 this.weight = weight;
29575 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29578 setIcon : function(icon)
29581 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29584 this.faicon = icon;
29586 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29607 * @class Roo.bootstrap.UploadCropbox
29608 * @extends Roo.bootstrap.Component
29609 * Bootstrap UploadCropbox class
29610 * @cfg {String} emptyText show when image has been loaded
29611 * @cfg {String} rotateNotify show when image too small to rotate
29612 * @cfg {Number} errorTimeout default 3000
29613 * @cfg {Number} minWidth default 300
29614 * @cfg {Number} minHeight default 300
29615 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29616 * @cfg {Boolean} isDocument (true|false) default false
29617 * @cfg {String} url action url
29618 * @cfg {String} paramName default 'imageUpload'
29619 * @cfg {String} method default POST
29620 * @cfg {Boolean} loadMask (true|false) default true
29621 * @cfg {Boolean} loadingText default 'Loading...'
29624 * Create a new UploadCropbox
29625 * @param {Object} config The config object
29628 Roo.bootstrap.UploadCropbox = function(config){
29629 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29633 * @event beforeselectfile
29634 * Fire before select file
29635 * @param {Roo.bootstrap.UploadCropbox} this
29637 "beforeselectfile" : true,
29640 * Fire after initEvent
29641 * @param {Roo.bootstrap.UploadCropbox} this
29646 * Fire after initEvent
29647 * @param {Roo.bootstrap.UploadCropbox} this
29648 * @param {String} data
29653 * Fire when preparing the file data
29654 * @param {Roo.bootstrap.UploadCropbox} this
29655 * @param {Object} file
29660 * Fire when get exception
29661 * @param {Roo.bootstrap.UploadCropbox} this
29662 * @param {XMLHttpRequest} xhr
29664 "exception" : true,
29666 * @event beforeloadcanvas
29667 * Fire before load the canvas
29668 * @param {Roo.bootstrap.UploadCropbox} this
29669 * @param {String} src
29671 "beforeloadcanvas" : true,
29674 * Fire when trash image
29675 * @param {Roo.bootstrap.UploadCropbox} this
29680 * Fire when download the image
29681 * @param {Roo.bootstrap.UploadCropbox} this
29685 * @event footerbuttonclick
29686 * Fire when footerbuttonclick
29687 * @param {Roo.bootstrap.UploadCropbox} this
29688 * @param {String} type
29690 "footerbuttonclick" : true,
29694 * @param {Roo.bootstrap.UploadCropbox} this
29699 * Fire when rotate the image
29700 * @param {Roo.bootstrap.UploadCropbox} this
29701 * @param {String} pos
29706 * Fire when inspect the file
29707 * @param {Roo.bootstrap.UploadCropbox} this
29708 * @param {Object} file
29713 * Fire when xhr upload the file
29714 * @param {Roo.bootstrap.UploadCropbox} this
29715 * @param {Object} data
29720 * Fire when arrange the file data
29721 * @param {Roo.bootstrap.UploadCropbox} this
29722 * @param {Object} formData
29727 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29730 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29732 emptyText : 'Click to upload image',
29733 rotateNotify : 'Image is too small to rotate',
29734 errorTimeout : 3000,
29748 cropType : 'image/jpeg',
29750 canvasLoaded : false,
29751 isDocument : false,
29753 paramName : 'imageUpload',
29755 loadingText : 'Loading...',
29758 getAutoCreate : function()
29762 cls : 'roo-upload-cropbox',
29766 cls : 'roo-upload-cropbox-selector',
29771 cls : 'roo-upload-cropbox-body',
29772 style : 'cursor:pointer',
29776 cls : 'roo-upload-cropbox-preview'
29780 cls : 'roo-upload-cropbox-thumb'
29784 cls : 'roo-upload-cropbox-empty-notify',
29785 html : this.emptyText
29789 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29790 html : this.rotateNotify
29796 cls : 'roo-upload-cropbox-footer',
29799 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29809 onRender : function(ct, position)
29811 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29813 if (this.buttons.length) {
29815 Roo.each(this.buttons, function(bb) {
29817 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29819 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29825 this.maskEl = this.el;
29829 initEvents : function()
29831 this.urlAPI = (window.createObjectURL && window) ||
29832 (window.URL && URL.revokeObjectURL && URL) ||
29833 (window.webkitURL && webkitURL);
29835 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29836 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29838 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29839 this.selectorEl.hide();
29841 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29842 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29845 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29846 this.thumbEl.hide();
29848 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29849 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29851 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29852 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29853 this.errorEl.hide();
29855 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29856 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29857 this.footerEl.hide();
29859 this.setThumbBoxSize();
29865 this.fireEvent('initial', this);
29872 window.addEventListener("resize", function() { _this.resize(); } );
29874 this.bodyEl.on('click', this.beforeSelectFile, this);
29877 this.bodyEl.on('touchstart', this.onTouchStart, this);
29878 this.bodyEl.on('touchmove', this.onTouchMove, this);
29879 this.bodyEl.on('touchend', this.onTouchEnd, this);
29883 this.bodyEl.on('mousedown', this.onMouseDown, this);
29884 this.bodyEl.on('mousemove', this.onMouseMove, this);
29885 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29886 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29887 Roo.get(document).on('mouseup', this.onMouseUp, this);
29890 this.selectorEl.on('change', this.onFileSelected, this);
29896 this.baseScale = 1;
29898 this.baseRotate = 1;
29899 this.dragable = false;
29900 this.pinching = false;
29903 this.cropData = false;
29904 this.notifyEl.dom.innerHTML = this.emptyText;
29906 this.selectorEl.dom.value = '';
29910 resize : function()
29912 if(this.fireEvent('resize', this) != false){
29913 this.setThumbBoxPosition();
29914 this.setCanvasPosition();
29918 onFooterButtonClick : function(e, el, o, type)
29921 case 'rotate-left' :
29922 this.onRotateLeft(e);
29924 case 'rotate-right' :
29925 this.onRotateRight(e);
29928 this.beforeSelectFile(e);
29943 this.fireEvent('footerbuttonclick', this, type);
29946 beforeSelectFile : function(e)
29948 e.preventDefault();
29950 if(this.fireEvent('beforeselectfile', this) != false){
29951 this.selectorEl.dom.click();
29955 onFileSelected : function(e)
29957 e.preventDefault();
29959 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29963 var file = this.selectorEl.dom.files[0];
29965 if(this.fireEvent('inspect', this, file) != false){
29966 this.prepare(file);
29971 trash : function(e)
29973 this.fireEvent('trash', this);
29976 download : function(e)
29978 this.fireEvent('download', this);
29981 loadCanvas : function(src)
29983 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29987 this.imageEl = document.createElement('img');
29991 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29993 this.imageEl.src = src;
29997 onLoadCanvas : function()
29999 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30000 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30002 this.bodyEl.un('click', this.beforeSelectFile, this);
30004 this.notifyEl.hide();
30005 this.thumbEl.show();
30006 this.footerEl.show();
30008 this.baseRotateLevel();
30010 if(this.isDocument){
30011 this.setThumbBoxSize();
30014 this.setThumbBoxPosition();
30016 this.baseScaleLevel();
30022 this.canvasLoaded = true;
30025 this.maskEl.unmask();
30030 setCanvasPosition : function()
30032 if(!this.canvasEl){
30036 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30037 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30039 this.previewEl.setLeft(pw);
30040 this.previewEl.setTop(ph);
30044 onMouseDown : function(e)
30048 this.dragable = true;
30049 this.pinching = false;
30051 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30052 this.dragable = false;
30056 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30057 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30061 onMouseMove : function(e)
30065 if(!this.canvasLoaded){
30069 if (!this.dragable){
30073 var minX = Math.ceil(this.thumbEl.getLeft(true));
30074 var minY = Math.ceil(this.thumbEl.getTop(true));
30076 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30077 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30079 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30080 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30082 x = x - this.mouseX;
30083 y = y - this.mouseY;
30085 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30086 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30088 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30089 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30091 this.previewEl.setLeft(bgX);
30092 this.previewEl.setTop(bgY);
30094 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30095 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30098 onMouseUp : function(e)
30102 this.dragable = false;
30105 onMouseWheel : function(e)
30109 this.startScale = this.scale;
30111 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30113 if(!this.zoomable()){
30114 this.scale = this.startScale;
30123 zoomable : function()
30125 var minScale = this.thumbEl.getWidth() / this.minWidth;
30127 if(this.minWidth < this.minHeight){
30128 minScale = this.thumbEl.getHeight() / this.minHeight;
30131 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30132 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30136 (this.rotate == 0 || this.rotate == 180) &&
30138 width > this.imageEl.OriginWidth ||
30139 height > this.imageEl.OriginHeight ||
30140 (width < this.minWidth && height < this.minHeight)
30148 (this.rotate == 90 || this.rotate == 270) &&
30150 width > this.imageEl.OriginWidth ||
30151 height > this.imageEl.OriginHeight ||
30152 (width < this.minHeight && height < this.minWidth)
30159 !this.isDocument &&
30160 (this.rotate == 0 || this.rotate == 180) &&
30162 width < this.minWidth ||
30163 width > this.imageEl.OriginWidth ||
30164 height < this.minHeight ||
30165 height > this.imageEl.OriginHeight
30172 !this.isDocument &&
30173 (this.rotate == 90 || this.rotate == 270) &&
30175 width < this.minHeight ||
30176 width > this.imageEl.OriginWidth ||
30177 height < this.minWidth ||
30178 height > this.imageEl.OriginHeight
30188 onRotateLeft : function(e)
30190 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30192 var minScale = this.thumbEl.getWidth() / this.minWidth;
30194 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30195 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30197 this.startScale = this.scale;
30199 while (this.getScaleLevel() < minScale){
30201 this.scale = this.scale + 1;
30203 if(!this.zoomable()){
30208 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30209 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30214 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30221 this.scale = this.startScale;
30223 this.onRotateFail();
30228 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30230 if(this.isDocument){
30231 this.setThumbBoxSize();
30232 this.setThumbBoxPosition();
30233 this.setCanvasPosition();
30238 this.fireEvent('rotate', this, 'left');
30242 onRotateRight : function(e)
30244 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30246 var minScale = this.thumbEl.getWidth() / this.minWidth;
30248 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30249 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30251 this.startScale = this.scale;
30253 while (this.getScaleLevel() < minScale){
30255 this.scale = this.scale + 1;
30257 if(!this.zoomable()){
30262 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30263 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30268 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30275 this.scale = this.startScale;
30277 this.onRotateFail();
30282 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30284 if(this.isDocument){
30285 this.setThumbBoxSize();
30286 this.setThumbBoxPosition();
30287 this.setCanvasPosition();
30292 this.fireEvent('rotate', this, 'right');
30295 onRotateFail : function()
30297 this.errorEl.show(true);
30301 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30306 this.previewEl.dom.innerHTML = '';
30308 var canvasEl = document.createElement("canvas");
30310 var contextEl = canvasEl.getContext("2d");
30312 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30313 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30314 var center = this.imageEl.OriginWidth / 2;
30316 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30317 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30318 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30319 center = this.imageEl.OriginHeight / 2;
30322 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30324 contextEl.translate(center, center);
30325 contextEl.rotate(this.rotate * Math.PI / 180);
30327 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30329 this.canvasEl = document.createElement("canvas");
30331 this.contextEl = this.canvasEl.getContext("2d");
30333 switch (this.rotate) {
30336 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30337 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30339 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30344 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30345 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30347 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30348 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);
30352 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30357 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30358 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30360 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30361 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);
30365 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);
30370 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30371 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30373 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30374 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30378 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);
30385 this.previewEl.appendChild(this.canvasEl);
30387 this.setCanvasPosition();
30392 if(!this.canvasLoaded){
30396 var imageCanvas = document.createElement("canvas");
30398 var imageContext = imageCanvas.getContext("2d");
30400 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30401 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30403 var center = imageCanvas.width / 2;
30405 imageContext.translate(center, center);
30407 imageContext.rotate(this.rotate * Math.PI / 180);
30409 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30411 var canvas = document.createElement("canvas");
30413 var context = canvas.getContext("2d");
30415 canvas.width = this.minWidth;
30416 canvas.height = this.minHeight;
30418 switch (this.rotate) {
30421 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30422 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30424 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30425 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30427 var targetWidth = this.minWidth - 2 * x;
30428 var targetHeight = this.minHeight - 2 * y;
30432 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30433 scale = targetWidth / width;
30436 if(x > 0 && y == 0){
30437 scale = targetHeight / height;
30440 if(x > 0 && y > 0){
30441 scale = targetWidth / width;
30443 if(width < height){
30444 scale = targetHeight / height;
30448 context.scale(scale, scale);
30450 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30451 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30453 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30454 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30456 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30461 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30462 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30464 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30465 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30467 var targetWidth = this.minWidth - 2 * x;
30468 var targetHeight = this.minHeight - 2 * y;
30472 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30473 scale = targetWidth / width;
30476 if(x > 0 && y == 0){
30477 scale = targetHeight / height;
30480 if(x > 0 && y > 0){
30481 scale = targetWidth / width;
30483 if(width < height){
30484 scale = targetHeight / height;
30488 context.scale(scale, scale);
30490 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30491 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30493 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30494 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30496 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30498 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30503 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30504 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30506 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30507 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30509 var targetWidth = this.minWidth - 2 * x;
30510 var targetHeight = this.minHeight - 2 * y;
30514 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30515 scale = targetWidth / width;
30518 if(x > 0 && y == 0){
30519 scale = targetHeight / height;
30522 if(x > 0 && y > 0){
30523 scale = targetWidth / width;
30525 if(width < height){
30526 scale = targetHeight / height;
30530 context.scale(scale, scale);
30532 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30533 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30535 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30536 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30538 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30539 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30541 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30546 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30547 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30549 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30550 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30552 var targetWidth = this.minWidth - 2 * x;
30553 var targetHeight = this.minHeight - 2 * y;
30557 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30558 scale = targetWidth / width;
30561 if(x > 0 && y == 0){
30562 scale = targetHeight / height;
30565 if(x > 0 && y > 0){
30566 scale = targetWidth / width;
30568 if(width < height){
30569 scale = targetHeight / height;
30573 context.scale(scale, scale);
30575 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30576 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30578 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30579 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30581 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30583 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30590 this.cropData = canvas.toDataURL(this.cropType);
30592 if(this.fireEvent('crop', this, this.cropData) !== false){
30593 this.process(this.file, this.cropData);
30600 setThumbBoxSize : function()
30604 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30605 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30606 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30608 this.minWidth = width;
30609 this.minHeight = height;
30611 if(this.rotate == 90 || this.rotate == 270){
30612 this.minWidth = height;
30613 this.minHeight = width;
30618 width = Math.ceil(this.minWidth * height / this.minHeight);
30620 if(this.minWidth > this.minHeight){
30622 height = Math.ceil(this.minHeight * width / this.minWidth);
30625 this.thumbEl.setStyle({
30626 width : width + 'px',
30627 height : height + 'px'
30634 setThumbBoxPosition : function()
30636 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30637 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30639 this.thumbEl.setLeft(x);
30640 this.thumbEl.setTop(y);
30644 baseRotateLevel : function()
30646 this.baseRotate = 1;
30649 typeof(this.exif) != 'undefined' &&
30650 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30651 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30653 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30656 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30660 baseScaleLevel : function()
30664 if(this.isDocument){
30666 if(this.baseRotate == 6 || this.baseRotate == 8){
30668 height = this.thumbEl.getHeight();
30669 this.baseScale = height / this.imageEl.OriginWidth;
30671 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30672 width = this.thumbEl.getWidth();
30673 this.baseScale = width / this.imageEl.OriginHeight;
30679 height = this.thumbEl.getHeight();
30680 this.baseScale = height / this.imageEl.OriginHeight;
30682 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30683 width = this.thumbEl.getWidth();
30684 this.baseScale = width / this.imageEl.OriginWidth;
30690 if(this.baseRotate == 6 || this.baseRotate == 8){
30692 width = this.thumbEl.getHeight();
30693 this.baseScale = width / this.imageEl.OriginHeight;
30695 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30696 height = this.thumbEl.getWidth();
30697 this.baseScale = height / this.imageEl.OriginHeight;
30700 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30701 height = this.thumbEl.getWidth();
30702 this.baseScale = height / this.imageEl.OriginHeight;
30704 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30705 width = this.thumbEl.getHeight();
30706 this.baseScale = width / this.imageEl.OriginWidth;
30713 width = this.thumbEl.getWidth();
30714 this.baseScale = width / this.imageEl.OriginWidth;
30716 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30717 height = this.thumbEl.getHeight();
30718 this.baseScale = height / this.imageEl.OriginHeight;
30721 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30723 height = this.thumbEl.getHeight();
30724 this.baseScale = height / this.imageEl.OriginHeight;
30726 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30727 width = this.thumbEl.getWidth();
30728 this.baseScale = width / this.imageEl.OriginWidth;
30736 getScaleLevel : function()
30738 return this.baseScale * Math.pow(1.1, this.scale);
30741 onTouchStart : function(e)
30743 if(!this.canvasLoaded){
30744 this.beforeSelectFile(e);
30748 var touches = e.browserEvent.touches;
30754 if(touches.length == 1){
30755 this.onMouseDown(e);
30759 if(touches.length != 2){
30765 for(var i = 0, finger; finger = touches[i]; i++){
30766 coords.push(finger.pageX, finger.pageY);
30769 var x = Math.pow(coords[0] - coords[2], 2);
30770 var y = Math.pow(coords[1] - coords[3], 2);
30772 this.startDistance = Math.sqrt(x + y);
30774 this.startScale = this.scale;
30776 this.pinching = true;
30777 this.dragable = false;
30781 onTouchMove : function(e)
30783 if(!this.pinching && !this.dragable){
30787 var touches = e.browserEvent.touches;
30794 this.onMouseMove(e);
30800 for(var i = 0, finger; finger = touches[i]; i++){
30801 coords.push(finger.pageX, finger.pageY);
30804 var x = Math.pow(coords[0] - coords[2], 2);
30805 var y = Math.pow(coords[1] - coords[3], 2);
30807 this.endDistance = Math.sqrt(x + y);
30809 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30811 if(!this.zoomable()){
30812 this.scale = this.startScale;
30820 onTouchEnd : function(e)
30822 this.pinching = false;
30823 this.dragable = false;
30827 process : function(file, crop)
30830 this.maskEl.mask(this.loadingText);
30833 this.xhr = new XMLHttpRequest();
30835 file.xhr = this.xhr;
30837 this.xhr.open(this.method, this.url, true);
30840 "Accept": "application/json",
30841 "Cache-Control": "no-cache",
30842 "X-Requested-With": "XMLHttpRequest"
30845 for (var headerName in headers) {
30846 var headerValue = headers[headerName];
30848 this.xhr.setRequestHeader(headerName, headerValue);
30854 this.xhr.onload = function()
30856 _this.xhrOnLoad(_this.xhr);
30859 this.xhr.onerror = function()
30861 _this.xhrOnError(_this.xhr);
30864 var formData = new FormData();
30866 formData.append('returnHTML', 'NO');
30869 formData.append('crop', crop);
30872 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30873 formData.append(this.paramName, file, file.name);
30876 if(typeof(file.filename) != 'undefined'){
30877 formData.append('filename', file.filename);
30880 if(typeof(file.mimetype) != 'undefined'){
30881 formData.append('mimetype', file.mimetype);
30884 if(this.fireEvent('arrange', this, formData) != false){
30885 this.xhr.send(formData);
30889 xhrOnLoad : function(xhr)
30892 this.maskEl.unmask();
30895 if (xhr.readyState !== 4) {
30896 this.fireEvent('exception', this, xhr);
30900 var response = Roo.decode(xhr.responseText);
30902 if(!response.success){
30903 this.fireEvent('exception', this, xhr);
30907 var response = Roo.decode(xhr.responseText);
30909 this.fireEvent('upload', this, response);
30913 xhrOnError : function()
30916 this.maskEl.unmask();
30919 Roo.log('xhr on error');
30921 var response = Roo.decode(xhr.responseText);
30927 prepare : function(file)
30930 this.maskEl.mask(this.loadingText);
30936 if(typeof(file) === 'string'){
30937 this.loadCanvas(file);
30941 if(!file || !this.urlAPI){
30946 this.cropType = file.type;
30950 if(this.fireEvent('prepare', this, this.file) != false){
30952 var reader = new FileReader();
30954 reader.onload = function (e) {
30955 if (e.target.error) {
30956 Roo.log(e.target.error);
30960 var buffer = e.target.result,
30961 dataView = new DataView(buffer),
30963 maxOffset = dataView.byteLength - 4,
30967 if (dataView.getUint16(0) === 0xffd8) {
30968 while (offset < maxOffset) {
30969 markerBytes = dataView.getUint16(offset);
30971 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30972 markerLength = dataView.getUint16(offset + 2) + 2;
30973 if (offset + markerLength > dataView.byteLength) {
30974 Roo.log('Invalid meta data: Invalid segment size.');
30978 if(markerBytes == 0xffe1){
30979 _this.parseExifData(
30986 offset += markerLength;
30996 var url = _this.urlAPI.createObjectURL(_this.file);
30998 _this.loadCanvas(url);
31003 reader.readAsArrayBuffer(this.file);
31009 parseExifData : function(dataView, offset, length)
31011 var tiffOffset = offset + 10,
31015 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31016 // No Exif data, might be XMP data instead
31020 // Check for the ASCII code for "Exif" (0x45786966):
31021 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31022 // No Exif data, might be XMP data instead
31025 if (tiffOffset + 8 > dataView.byteLength) {
31026 Roo.log('Invalid Exif data: Invalid segment size.');
31029 // Check for the two null bytes:
31030 if (dataView.getUint16(offset + 8) !== 0x0000) {
31031 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31034 // Check the byte alignment:
31035 switch (dataView.getUint16(tiffOffset)) {
31037 littleEndian = true;
31040 littleEndian = false;
31043 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31046 // Check for the TIFF tag marker (0x002A):
31047 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31048 Roo.log('Invalid Exif data: Missing TIFF marker.');
31051 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31052 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31054 this.parseExifTags(
31057 tiffOffset + dirOffset,
31062 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31067 if (dirOffset + 6 > dataView.byteLength) {
31068 Roo.log('Invalid Exif data: Invalid directory offset.');
31071 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31072 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31073 if (dirEndOffset + 4 > dataView.byteLength) {
31074 Roo.log('Invalid Exif data: Invalid directory size.');
31077 for (i = 0; i < tagsNumber; i += 1) {
31081 dirOffset + 2 + 12 * i, // tag offset
31085 // Return the offset to the next directory:
31086 return dataView.getUint32(dirEndOffset, littleEndian);
31089 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31091 var tag = dataView.getUint16(offset, littleEndian);
31093 this.exif[tag] = this.getExifValue(
31097 dataView.getUint16(offset + 2, littleEndian), // tag type
31098 dataView.getUint32(offset + 4, littleEndian), // tag length
31103 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31105 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31114 Roo.log('Invalid Exif data: Invalid tag type.');
31118 tagSize = tagType.size * length;
31119 // Determine if the value is contained in the dataOffset bytes,
31120 // or if the value at the dataOffset is a pointer to the actual data:
31121 dataOffset = tagSize > 4 ?
31122 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31123 if (dataOffset + tagSize > dataView.byteLength) {
31124 Roo.log('Invalid Exif data: Invalid data offset.');
31127 if (length === 1) {
31128 return tagType.getValue(dataView, dataOffset, littleEndian);
31131 for (i = 0; i < length; i += 1) {
31132 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31135 if (tagType.ascii) {
31137 // Concatenate the chars:
31138 for (i = 0; i < values.length; i += 1) {
31140 // Ignore the terminating NULL byte(s):
31141 if (c === '\u0000') {
31153 Roo.apply(Roo.bootstrap.UploadCropbox, {
31155 'Orientation': 0x0112
31159 1: 0, //'top-left',
31161 3: 180, //'bottom-right',
31162 // 4: 'bottom-left',
31164 6: 90, //'right-top',
31165 // 7: 'right-bottom',
31166 8: 270 //'left-bottom'
31170 // byte, 8-bit unsigned int:
31172 getValue: function (dataView, dataOffset) {
31173 return dataView.getUint8(dataOffset);
31177 // ascii, 8-bit byte:
31179 getValue: function (dataView, dataOffset) {
31180 return String.fromCharCode(dataView.getUint8(dataOffset));
31185 // short, 16 bit int:
31187 getValue: function (dataView, dataOffset, littleEndian) {
31188 return dataView.getUint16(dataOffset, littleEndian);
31192 // long, 32 bit int:
31194 getValue: function (dataView, dataOffset, littleEndian) {
31195 return dataView.getUint32(dataOffset, littleEndian);
31199 // rational = two long values, first is numerator, second is denominator:
31201 getValue: function (dataView, dataOffset, littleEndian) {
31202 return dataView.getUint32(dataOffset, littleEndian) /
31203 dataView.getUint32(dataOffset + 4, littleEndian);
31207 // slong, 32 bit signed int:
31209 getValue: function (dataView, dataOffset, littleEndian) {
31210 return dataView.getInt32(dataOffset, littleEndian);
31214 // srational, two slongs, first is numerator, second is denominator:
31216 getValue: function (dataView, dataOffset, littleEndian) {
31217 return dataView.getInt32(dataOffset, littleEndian) /
31218 dataView.getInt32(dataOffset + 4, littleEndian);
31228 cls : 'btn-group roo-upload-cropbox-rotate-left',
31229 action : 'rotate-left',
31233 cls : 'btn btn-default',
31234 html : '<i class="fa fa-undo"></i>'
31240 cls : 'btn-group roo-upload-cropbox-picture',
31241 action : 'picture',
31245 cls : 'btn btn-default',
31246 html : '<i class="fa fa-picture-o"></i>'
31252 cls : 'btn-group roo-upload-cropbox-rotate-right',
31253 action : 'rotate-right',
31257 cls : 'btn btn-default',
31258 html : '<i class="fa fa-repeat"></i>'
31266 cls : 'btn-group roo-upload-cropbox-rotate-left',
31267 action : 'rotate-left',
31271 cls : 'btn btn-default',
31272 html : '<i class="fa fa-undo"></i>'
31278 cls : 'btn-group roo-upload-cropbox-download',
31279 action : 'download',
31283 cls : 'btn btn-default',
31284 html : '<i class="fa fa-download"></i>'
31290 cls : 'btn-group roo-upload-cropbox-crop',
31295 cls : 'btn btn-default',
31296 html : '<i class="fa fa-crop"></i>'
31302 cls : 'btn-group roo-upload-cropbox-trash',
31307 cls : 'btn btn-default',
31308 html : '<i class="fa fa-trash"></i>'
31314 cls : 'btn-group roo-upload-cropbox-rotate-right',
31315 action : 'rotate-right',
31319 cls : 'btn btn-default',
31320 html : '<i class="fa fa-repeat"></i>'
31328 cls : 'btn-group roo-upload-cropbox-rotate-left',
31329 action : 'rotate-left',
31333 cls : 'btn btn-default',
31334 html : '<i class="fa fa-undo"></i>'
31340 cls : 'btn-group roo-upload-cropbox-rotate-right',
31341 action : 'rotate-right',
31345 cls : 'btn btn-default',
31346 html : '<i class="fa fa-repeat"></i>'
31359 * @class Roo.bootstrap.DocumentManager
31360 * @extends Roo.bootstrap.Component
31361 * Bootstrap DocumentManager class
31362 * @cfg {String} paramName default 'imageUpload'
31363 * @cfg {String} toolTipName default 'filename'
31364 * @cfg {String} method default POST
31365 * @cfg {String} url action url
31366 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31367 * @cfg {Boolean} multiple multiple upload default true
31368 * @cfg {Number} thumbSize default 300
31369 * @cfg {String} fieldLabel
31370 * @cfg {Number} labelWidth default 4
31371 * @cfg {String} labelAlign (left|top) default left
31372 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31373 * @cfg {Number} labellg set the width of label (1-12)
31374 * @cfg {Number} labelmd set the width of label (1-12)
31375 * @cfg {Number} labelsm set the width of label (1-12)
31376 * @cfg {Number} labelxs set the width of label (1-12)
31379 * Create a new DocumentManager
31380 * @param {Object} config The config object
31383 Roo.bootstrap.DocumentManager = function(config){
31384 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31387 this.delegates = [];
31392 * Fire when initial the DocumentManager
31393 * @param {Roo.bootstrap.DocumentManager} this
31398 * inspect selected file
31399 * @param {Roo.bootstrap.DocumentManager} this
31400 * @param {File} file
31405 * Fire when xhr load exception
31406 * @param {Roo.bootstrap.DocumentManager} this
31407 * @param {XMLHttpRequest} xhr
31409 "exception" : true,
31411 * @event afterupload
31412 * Fire when xhr load exception
31413 * @param {Roo.bootstrap.DocumentManager} this
31414 * @param {XMLHttpRequest} xhr
31416 "afterupload" : true,
31419 * prepare the form data
31420 * @param {Roo.bootstrap.DocumentManager} this
31421 * @param {Object} formData
31426 * Fire when remove the file
31427 * @param {Roo.bootstrap.DocumentManager} this
31428 * @param {Object} file
31433 * Fire after refresh the file
31434 * @param {Roo.bootstrap.DocumentManager} this
31439 * Fire after click the image
31440 * @param {Roo.bootstrap.DocumentManager} this
31441 * @param {Object} file
31446 * Fire when upload a image and editable set to true
31447 * @param {Roo.bootstrap.DocumentManager} this
31448 * @param {Object} file
31452 * @event beforeselectfile
31453 * Fire before select file
31454 * @param {Roo.bootstrap.DocumentManager} this
31456 "beforeselectfile" : true,
31459 * Fire before process file
31460 * @param {Roo.bootstrap.DocumentManager} this
31461 * @param {Object} file
31465 * @event previewrendered
31466 * Fire when preview rendered
31467 * @param {Roo.bootstrap.DocumentManager} this
31468 * @param {Object} file
31470 "previewrendered" : true,
31473 "previewResize" : true
31478 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31487 paramName : 'imageUpload',
31488 toolTipName : 'filename',
31491 labelAlign : 'left',
31501 getAutoCreate : function()
31503 var managerWidget = {
31505 cls : 'roo-document-manager',
31509 cls : 'roo-document-manager-selector',
31514 cls : 'roo-document-manager-uploader',
31518 cls : 'roo-document-manager-upload-btn',
31519 html : '<i class="fa fa-plus"></i>'
31530 cls : 'column col-md-12',
31535 if(this.fieldLabel.length){
31540 cls : 'column col-md-12',
31541 html : this.fieldLabel
31545 cls : 'column col-md-12',
31550 if(this.labelAlign == 'left'){
31555 html : this.fieldLabel
31564 if(this.labelWidth > 12){
31565 content[0].style = "width: " + this.labelWidth + 'px';
31568 if(this.labelWidth < 13 && this.labelmd == 0){
31569 this.labelmd = this.labelWidth;
31572 if(this.labellg > 0){
31573 content[0].cls += ' col-lg-' + this.labellg;
31574 content[1].cls += ' col-lg-' + (12 - this.labellg);
31577 if(this.labelmd > 0){
31578 content[0].cls += ' col-md-' + this.labelmd;
31579 content[1].cls += ' col-md-' + (12 - this.labelmd);
31582 if(this.labelsm > 0){
31583 content[0].cls += ' col-sm-' + this.labelsm;
31584 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31587 if(this.labelxs > 0){
31588 content[0].cls += ' col-xs-' + this.labelxs;
31589 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31597 cls : 'row clearfix',
31605 initEvents : function()
31607 this.managerEl = this.el.select('.roo-document-manager', true).first();
31608 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31610 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31611 this.selectorEl.hide();
31614 this.selectorEl.attr('multiple', 'multiple');
31617 this.selectorEl.on('change', this.onFileSelected, this);
31619 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31620 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31622 this.uploader.on('click', this.onUploaderClick, this);
31624 this.renderProgressDialog();
31628 window.addEventListener("resize", function() { _this.refresh(); } );
31630 this.fireEvent('initial', this);
31633 renderProgressDialog : function()
31637 this.progressDialog = new Roo.bootstrap.Modal({
31638 cls : 'roo-document-manager-progress-dialog',
31639 allow_close : false,
31650 btnclick : function() {
31651 _this.uploadCancel();
31657 this.progressDialog.render(Roo.get(document.body));
31659 this.progress = new Roo.bootstrap.Progress({
31660 cls : 'roo-document-manager-progress',
31665 this.progress.render(this.progressDialog.getChildContainer());
31667 this.progressBar = new Roo.bootstrap.ProgressBar({
31668 cls : 'roo-document-manager-progress-bar',
31671 aria_valuemax : 12,
31675 this.progressBar.render(this.progress.getChildContainer());
31678 onUploaderClick : function(e)
31680 e.preventDefault();
31682 if(this.fireEvent('beforeselectfile', this) != false){
31683 this.selectorEl.dom.click();
31688 onFileSelected : function(e)
31690 e.preventDefault();
31692 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31696 Roo.each(this.selectorEl.dom.files, function(file){
31697 if(this.fireEvent('inspect', this, file) != false){
31698 this.files.push(file);
31708 this.selectorEl.dom.value = '';
31710 if(!this.files || !this.files.length){
31714 if(this.boxes > 0 && this.files.length > this.boxes){
31715 this.files = this.files.slice(0, this.boxes);
31718 this.uploader.show();
31720 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31721 this.uploader.hide();
31730 Roo.each(this.files, function(file){
31732 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31733 var f = this.renderPreview(file);
31738 if(file.type.indexOf('image') != -1){
31739 this.delegates.push(
31741 _this.process(file);
31742 }).createDelegate(this)
31750 _this.process(file);
31751 }).createDelegate(this)
31756 this.files = files;
31758 this.delegates = this.delegates.concat(docs);
31760 if(!this.delegates.length){
31765 this.progressBar.aria_valuemax = this.delegates.length;
31772 arrange : function()
31774 if(!this.delegates.length){
31775 this.progressDialog.hide();
31780 var delegate = this.delegates.shift();
31782 this.progressDialog.show();
31784 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31786 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31791 refresh : function()
31793 this.uploader.show();
31795 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31796 this.uploader.hide();
31799 Roo.isTouch ? this.closable(false) : this.closable(true);
31801 this.fireEvent('refresh', this);
31804 onRemove : function(e, el, o)
31806 e.preventDefault();
31808 this.fireEvent('remove', this, o);
31812 remove : function(o)
31816 Roo.each(this.files, function(file){
31817 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31826 this.files = files;
31833 Roo.each(this.files, function(file){
31838 file.target.remove();
31847 onClick : function(e, el, o)
31849 e.preventDefault();
31851 this.fireEvent('click', this, o);
31855 closable : function(closable)
31857 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31859 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31871 xhrOnLoad : function(xhr)
31873 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31877 if (xhr.readyState !== 4) {
31879 this.fireEvent('exception', this, xhr);
31883 var response = Roo.decode(xhr.responseText);
31885 if(!response.success){
31887 this.fireEvent('exception', this, xhr);
31891 var file = this.renderPreview(response.data);
31893 this.files.push(file);
31897 this.fireEvent('afterupload', this, xhr);
31901 xhrOnError : function(xhr)
31903 Roo.log('xhr on error');
31905 var response = Roo.decode(xhr.responseText);
31912 process : function(file)
31914 if(this.fireEvent('process', this, file) !== false){
31915 if(this.editable && file.type.indexOf('image') != -1){
31916 this.fireEvent('edit', this, file);
31920 this.uploadStart(file, false);
31927 uploadStart : function(file, crop)
31929 this.xhr = new XMLHttpRequest();
31931 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31936 file.xhr = this.xhr;
31938 this.managerEl.createChild({
31940 cls : 'roo-document-manager-loading',
31944 tooltip : file.name,
31945 cls : 'roo-document-manager-thumb',
31946 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31952 this.xhr.open(this.method, this.url, true);
31955 "Accept": "application/json",
31956 "Cache-Control": "no-cache",
31957 "X-Requested-With": "XMLHttpRequest"
31960 for (var headerName in headers) {
31961 var headerValue = headers[headerName];
31963 this.xhr.setRequestHeader(headerName, headerValue);
31969 this.xhr.onload = function()
31971 _this.xhrOnLoad(_this.xhr);
31974 this.xhr.onerror = function()
31976 _this.xhrOnError(_this.xhr);
31979 var formData = new FormData();
31981 formData.append('returnHTML', 'NO');
31984 formData.append('crop', crop);
31987 formData.append(this.paramName, file, file.name);
31994 if(this.fireEvent('prepare', this, formData, options) != false){
31996 if(options.manually){
32000 this.xhr.send(formData);
32004 this.uploadCancel();
32007 uploadCancel : function()
32013 this.delegates = [];
32015 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32022 renderPreview : function(file)
32024 if(typeof(file.target) != 'undefined' && file.target){
32028 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32030 var previewEl = this.managerEl.createChild({
32032 cls : 'roo-document-manager-preview',
32036 tooltip : file[this.toolTipName],
32037 cls : 'roo-document-manager-thumb',
32038 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32043 html : '<i class="fa fa-times-circle"></i>'
32048 var close = previewEl.select('button.close', true).first();
32050 close.on('click', this.onRemove, this, file);
32052 file.target = previewEl;
32054 var image = previewEl.select('img', true).first();
32058 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32060 image.on('click', this.onClick, this, file);
32062 this.fireEvent('previewrendered', this, file);
32068 onPreviewLoad : function(file, image)
32070 if(typeof(file.target) == 'undefined' || !file.target){
32074 var width = image.dom.naturalWidth || image.dom.width;
32075 var height = image.dom.naturalHeight || image.dom.height;
32077 if(!this.previewResize) {
32081 if(width > height){
32082 file.target.addClass('wide');
32086 file.target.addClass('tall');
32091 uploadFromSource : function(file, crop)
32093 this.xhr = new XMLHttpRequest();
32095 this.managerEl.createChild({
32097 cls : 'roo-document-manager-loading',
32101 tooltip : file.name,
32102 cls : 'roo-document-manager-thumb',
32103 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32109 this.xhr.open(this.method, this.url, true);
32112 "Accept": "application/json",
32113 "Cache-Control": "no-cache",
32114 "X-Requested-With": "XMLHttpRequest"
32117 for (var headerName in headers) {
32118 var headerValue = headers[headerName];
32120 this.xhr.setRequestHeader(headerName, headerValue);
32126 this.xhr.onload = function()
32128 _this.xhrOnLoad(_this.xhr);
32131 this.xhr.onerror = function()
32133 _this.xhrOnError(_this.xhr);
32136 var formData = new FormData();
32138 formData.append('returnHTML', 'NO');
32140 formData.append('crop', crop);
32142 if(typeof(file.filename) != 'undefined'){
32143 formData.append('filename', file.filename);
32146 if(typeof(file.mimetype) != 'undefined'){
32147 formData.append('mimetype', file.mimetype);
32152 if(this.fireEvent('prepare', this, formData) != false){
32153 this.xhr.send(formData);
32163 * @class Roo.bootstrap.DocumentViewer
32164 * @extends Roo.bootstrap.Component
32165 * Bootstrap DocumentViewer class
32166 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32167 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32170 * Create a new DocumentViewer
32171 * @param {Object} config The config object
32174 Roo.bootstrap.DocumentViewer = function(config){
32175 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32180 * Fire after initEvent
32181 * @param {Roo.bootstrap.DocumentViewer} this
32187 * @param {Roo.bootstrap.DocumentViewer} this
32192 * Fire after download button
32193 * @param {Roo.bootstrap.DocumentViewer} this
32198 * Fire after trash button
32199 * @param {Roo.bootstrap.DocumentViewer} this
32206 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32208 showDownload : true,
32212 getAutoCreate : function()
32216 cls : 'roo-document-viewer',
32220 cls : 'roo-document-viewer-body',
32224 cls : 'roo-document-viewer-thumb',
32228 cls : 'roo-document-viewer-image'
32236 cls : 'roo-document-viewer-footer',
32239 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32243 cls : 'btn-group roo-document-viewer-download',
32247 cls : 'btn btn-default',
32248 html : '<i class="fa fa-download"></i>'
32254 cls : 'btn-group roo-document-viewer-trash',
32258 cls : 'btn btn-default',
32259 html : '<i class="fa fa-trash"></i>'
32272 initEvents : function()
32274 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32275 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32277 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32278 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32280 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32281 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32283 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32284 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32286 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32287 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32289 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32290 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32292 this.bodyEl.on('click', this.onClick, this);
32293 this.downloadBtn.on('click', this.onDownload, this);
32294 this.trashBtn.on('click', this.onTrash, this);
32296 this.downloadBtn.hide();
32297 this.trashBtn.hide();
32299 if(this.showDownload){
32300 this.downloadBtn.show();
32303 if(this.showTrash){
32304 this.trashBtn.show();
32307 if(!this.showDownload && !this.showTrash) {
32308 this.footerEl.hide();
32313 initial : function()
32315 this.fireEvent('initial', this);
32319 onClick : function(e)
32321 e.preventDefault();
32323 this.fireEvent('click', this);
32326 onDownload : function(e)
32328 e.preventDefault();
32330 this.fireEvent('download', this);
32333 onTrash : function(e)
32335 e.preventDefault();
32337 this.fireEvent('trash', this);
32349 * @class Roo.bootstrap.NavProgressBar
32350 * @extends Roo.bootstrap.Component
32351 * Bootstrap NavProgressBar class
32354 * Create a new nav progress bar
32355 * @param {Object} config The config object
32358 Roo.bootstrap.NavProgressBar = function(config){
32359 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32361 this.bullets = this.bullets || [];
32363 // Roo.bootstrap.NavProgressBar.register(this);
32367 * Fires when the active item changes
32368 * @param {Roo.bootstrap.NavProgressBar} this
32369 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32370 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32377 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32382 getAutoCreate : function()
32384 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32388 cls : 'roo-navigation-bar-group',
32392 cls : 'roo-navigation-top-bar'
32396 cls : 'roo-navigation-bullets-bar',
32400 cls : 'roo-navigation-bar'
32407 cls : 'roo-navigation-bottom-bar'
32417 initEvents: function()
32422 onRender : function(ct, position)
32424 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32426 if(this.bullets.length){
32427 Roo.each(this.bullets, function(b){
32436 addItem : function(cfg)
32438 var item = new Roo.bootstrap.NavProgressItem(cfg);
32440 item.parentId = this.id;
32441 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32444 var top = new Roo.bootstrap.Element({
32446 cls : 'roo-navigation-bar-text'
32449 var bottom = new Roo.bootstrap.Element({
32451 cls : 'roo-navigation-bar-text'
32454 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32455 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32457 var topText = new Roo.bootstrap.Element({
32459 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32462 var bottomText = new Roo.bootstrap.Element({
32464 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32467 topText.onRender(top.el, null);
32468 bottomText.onRender(bottom.el, null);
32471 item.bottomEl = bottom;
32474 this.barItems.push(item);
32479 getActive : function()
32481 var active = false;
32483 Roo.each(this.barItems, function(v){
32485 if (!v.isActive()) {
32497 setActiveItem : function(item)
32501 Roo.each(this.barItems, function(v){
32502 if (v.rid == item.rid) {
32506 if (v.isActive()) {
32507 v.setActive(false);
32512 item.setActive(true);
32514 this.fireEvent('changed', this, item, prev);
32517 getBarItem: function(rid)
32521 Roo.each(this.barItems, function(e) {
32522 if (e.rid != rid) {
32533 indexOfItem : function(item)
32537 Roo.each(this.barItems, function(v, i){
32539 if (v.rid != item.rid) {
32550 setActiveNext : function()
32552 var i = this.indexOfItem(this.getActive());
32554 if (i > this.barItems.length) {
32558 this.setActiveItem(this.barItems[i+1]);
32561 setActivePrev : function()
32563 var i = this.indexOfItem(this.getActive());
32569 this.setActiveItem(this.barItems[i-1]);
32572 format : function()
32574 if(!this.barItems.length){
32578 var width = 100 / this.barItems.length;
32580 Roo.each(this.barItems, function(i){
32581 i.el.setStyle('width', width + '%');
32582 i.topEl.el.setStyle('width', width + '%');
32583 i.bottomEl.el.setStyle('width', width + '%');
32592 * Nav Progress Item
32597 * @class Roo.bootstrap.NavProgressItem
32598 * @extends Roo.bootstrap.Component
32599 * Bootstrap NavProgressItem class
32600 * @cfg {String} rid the reference id
32601 * @cfg {Boolean} active (true|false) Is item active default false
32602 * @cfg {Boolean} disabled (true|false) Is item active default false
32603 * @cfg {String} html
32604 * @cfg {String} position (top|bottom) text position default bottom
32605 * @cfg {String} icon show icon instead of number
32608 * Create a new NavProgressItem
32609 * @param {Object} config The config object
32611 Roo.bootstrap.NavProgressItem = function(config){
32612 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32617 * The raw click event for the entire grid.
32618 * @param {Roo.bootstrap.NavProgressItem} this
32619 * @param {Roo.EventObject} e
32626 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32632 position : 'bottom',
32635 getAutoCreate : function()
32637 var iconCls = 'roo-navigation-bar-item-icon';
32639 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32643 cls: 'roo-navigation-bar-item',
32653 cfg.cls += ' active';
32656 cfg.cls += ' disabled';
32662 disable : function()
32664 this.setDisabled(true);
32667 enable : function()
32669 this.setDisabled(false);
32672 initEvents: function()
32674 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32676 this.iconEl.on('click', this.onClick, this);
32679 onClick : function(e)
32681 e.preventDefault();
32687 if(this.fireEvent('click', this, e) === false){
32691 this.parent().setActiveItem(this);
32694 isActive: function ()
32696 return this.active;
32699 setActive : function(state)
32701 if(this.active == state){
32705 this.active = state;
32708 this.el.addClass('active');
32712 this.el.removeClass('active');
32717 setDisabled : function(state)
32719 if(this.disabled == state){
32723 this.disabled = state;
32726 this.el.addClass('disabled');
32730 this.el.removeClass('disabled');
32733 tooltipEl : function()
32735 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32748 * @class Roo.bootstrap.FieldLabel
32749 * @extends Roo.bootstrap.Component
32750 * Bootstrap FieldLabel class
32751 * @cfg {String} html contents of the element
32752 * @cfg {String} tag tag of the element default label
32753 * @cfg {String} cls class of the element
32754 * @cfg {String} target label target
32755 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32756 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32757 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32758 * @cfg {String} iconTooltip default "This field is required"
32759 * @cfg {String} indicatorpos (left|right) default left
32762 * Create a new FieldLabel
32763 * @param {Object} config The config object
32766 Roo.bootstrap.FieldLabel = function(config){
32767 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32772 * Fires after the field has been marked as invalid.
32773 * @param {Roo.form.FieldLabel} this
32774 * @param {String} msg The validation message
32779 * Fires after the field has been validated with no errors.
32780 * @param {Roo.form.FieldLabel} this
32786 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32793 invalidClass : 'has-warning',
32794 validClass : 'has-success',
32795 iconTooltip : 'This field is required',
32796 indicatorpos : 'left',
32798 getAutoCreate : function(){
32801 if (!this.allowBlank) {
32807 cls : 'roo-bootstrap-field-label ' + this.cls,
32812 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32813 tooltip : this.iconTooltip
32822 if(this.indicatorpos == 'right'){
32825 cls : 'roo-bootstrap-field-label ' + this.cls,
32834 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32835 tooltip : this.iconTooltip
32844 initEvents: function()
32846 Roo.bootstrap.Element.superclass.initEvents.call(this);
32848 this.indicator = this.indicatorEl();
32850 if(this.indicator){
32851 this.indicator.removeClass('visible');
32852 this.indicator.addClass('invisible');
32855 Roo.bootstrap.FieldLabel.register(this);
32858 indicatorEl : function()
32860 var indicator = this.el.select('i.roo-required-indicator',true).first();
32871 * Mark this field as valid
32873 markValid : function()
32875 if(this.indicator){
32876 this.indicator.removeClass('visible');
32877 this.indicator.addClass('invisible');
32879 if (Roo.bootstrap.version == 3) {
32880 this.el.removeClass(this.invalidClass);
32881 this.el.addClass(this.validClass);
32883 this.el.removeClass('is-invalid');
32884 this.el.addClass('is-valid');
32888 this.fireEvent('valid', this);
32892 * Mark this field as invalid
32893 * @param {String} msg The validation message
32895 markInvalid : function(msg)
32897 if(this.indicator){
32898 this.indicator.removeClass('invisible');
32899 this.indicator.addClass('visible');
32901 if (Roo.bootstrap.version == 3) {
32902 this.el.removeClass(this.validClass);
32903 this.el.addClass(this.invalidClass);
32905 this.el.removeClass('is-valid');
32906 this.el.addClass('is-invalid');
32910 this.fireEvent('invalid', this, msg);
32916 Roo.apply(Roo.bootstrap.FieldLabel, {
32921 * register a FieldLabel Group
32922 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32924 register : function(label)
32926 if(this.groups.hasOwnProperty(label.target)){
32930 this.groups[label.target] = label;
32934 * fetch a FieldLabel Group based on the target
32935 * @param {string} target
32936 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32938 get: function(target) {
32939 if (typeof(this.groups[target]) == 'undefined') {
32943 return this.groups[target] ;
32952 * page DateSplitField.
32958 * @class Roo.bootstrap.DateSplitField
32959 * @extends Roo.bootstrap.Component
32960 * Bootstrap DateSplitField class
32961 * @cfg {string} fieldLabel - the label associated
32962 * @cfg {Number} labelWidth set the width of label (0-12)
32963 * @cfg {String} labelAlign (top|left)
32964 * @cfg {Boolean} dayAllowBlank (true|false) default false
32965 * @cfg {Boolean} monthAllowBlank (true|false) default false
32966 * @cfg {Boolean} yearAllowBlank (true|false) default false
32967 * @cfg {string} dayPlaceholder
32968 * @cfg {string} monthPlaceholder
32969 * @cfg {string} yearPlaceholder
32970 * @cfg {string} dayFormat default 'd'
32971 * @cfg {string} monthFormat default 'm'
32972 * @cfg {string} yearFormat default 'Y'
32973 * @cfg {Number} labellg set the width of label (1-12)
32974 * @cfg {Number} labelmd set the width of label (1-12)
32975 * @cfg {Number} labelsm set the width of label (1-12)
32976 * @cfg {Number} labelxs set the width of label (1-12)
32980 * Create a new DateSplitField
32981 * @param {Object} config The config object
32984 Roo.bootstrap.DateSplitField = function(config){
32985 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32991 * getting the data of years
32992 * @param {Roo.bootstrap.DateSplitField} this
32993 * @param {Object} years
32998 * getting the data of days
32999 * @param {Roo.bootstrap.DateSplitField} this
33000 * @param {Object} days
33005 * Fires after the field has been marked as invalid.
33006 * @param {Roo.form.Field} this
33007 * @param {String} msg The validation message
33012 * Fires after the field has been validated with no errors.
33013 * @param {Roo.form.Field} this
33019 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33022 labelAlign : 'top',
33024 dayAllowBlank : false,
33025 monthAllowBlank : false,
33026 yearAllowBlank : false,
33027 dayPlaceholder : '',
33028 monthPlaceholder : '',
33029 yearPlaceholder : '',
33033 isFormField : true,
33039 getAutoCreate : function()
33043 cls : 'row roo-date-split-field-group',
33048 cls : 'form-hidden-field roo-date-split-field-group-value',
33054 var labelCls = 'col-md-12';
33055 var contentCls = 'col-md-4';
33057 if(this.fieldLabel){
33061 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33065 html : this.fieldLabel
33070 if(this.labelAlign == 'left'){
33072 if(this.labelWidth > 12){
33073 label.style = "width: " + this.labelWidth + 'px';
33076 if(this.labelWidth < 13 && this.labelmd == 0){
33077 this.labelmd = this.labelWidth;
33080 if(this.labellg > 0){
33081 labelCls = ' col-lg-' + this.labellg;
33082 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33085 if(this.labelmd > 0){
33086 labelCls = ' col-md-' + this.labelmd;
33087 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33090 if(this.labelsm > 0){
33091 labelCls = ' col-sm-' + this.labelsm;
33092 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33095 if(this.labelxs > 0){
33096 labelCls = ' col-xs-' + this.labelxs;
33097 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33101 label.cls += ' ' + labelCls;
33103 cfg.cn.push(label);
33106 Roo.each(['day', 'month', 'year'], function(t){
33109 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33116 inputEl: function ()
33118 return this.el.select('.roo-date-split-field-group-value', true).first();
33121 onRender : function(ct, position)
33125 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33127 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33129 this.dayField = new Roo.bootstrap.ComboBox({
33130 allowBlank : this.dayAllowBlank,
33131 alwaysQuery : true,
33132 displayField : 'value',
33135 forceSelection : true,
33137 placeholder : this.dayPlaceholder,
33138 selectOnFocus : true,
33139 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33140 triggerAction : 'all',
33142 valueField : 'value',
33143 store : new Roo.data.SimpleStore({
33144 data : (function() {
33146 _this.fireEvent('days', _this, days);
33149 fields : [ 'value' ]
33152 select : function (_self, record, index)
33154 _this.setValue(_this.getValue());
33159 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33161 this.monthField = new Roo.bootstrap.MonthField({
33162 after : '<i class=\"fa fa-calendar\"></i>',
33163 allowBlank : this.monthAllowBlank,
33164 placeholder : this.monthPlaceholder,
33167 render : function (_self)
33169 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33170 e.preventDefault();
33174 select : function (_self, oldvalue, newvalue)
33176 _this.setValue(_this.getValue());
33181 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33183 this.yearField = new Roo.bootstrap.ComboBox({
33184 allowBlank : this.yearAllowBlank,
33185 alwaysQuery : true,
33186 displayField : 'value',
33189 forceSelection : true,
33191 placeholder : this.yearPlaceholder,
33192 selectOnFocus : true,
33193 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33194 triggerAction : 'all',
33196 valueField : 'value',
33197 store : new Roo.data.SimpleStore({
33198 data : (function() {
33200 _this.fireEvent('years', _this, years);
33203 fields : [ 'value' ]
33206 select : function (_self, record, index)
33208 _this.setValue(_this.getValue());
33213 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33216 setValue : function(v, format)
33218 this.inputEl.dom.value = v;
33220 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33222 var d = Date.parseDate(v, f);
33229 this.setDay(d.format(this.dayFormat));
33230 this.setMonth(d.format(this.monthFormat));
33231 this.setYear(d.format(this.yearFormat));
33238 setDay : function(v)
33240 this.dayField.setValue(v);
33241 this.inputEl.dom.value = this.getValue();
33246 setMonth : function(v)
33248 this.monthField.setValue(v, true);
33249 this.inputEl.dom.value = this.getValue();
33254 setYear : function(v)
33256 this.yearField.setValue(v);
33257 this.inputEl.dom.value = this.getValue();
33262 getDay : function()
33264 return this.dayField.getValue();
33267 getMonth : function()
33269 return this.monthField.getValue();
33272 getYear : function()
33274 return this.yearField.getValue();
33277 getValue : function()
33279 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33281 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33291 this.inputEl.dom.value = '';
33296 validate : function()
33298 var d = this.dayField.validate();
33299 var m = this.monthField.validate();
33300 var y = this.yearField.validate();
33305 (!this.dayAllowBlank && !d) ||
33306 (!this.monthAllowBlank && !m) ||
33307 (!this.yearAllowBlank && !y)
33312 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33321 this.markInvalid();
33326 markValid : function()
33329 var label = this.el.select('label', true).first();
33330 var icon = this.el.select('i.fa-star', true).first();
33336 this.fireEvent('valid', this);
33340 * Mark this field as invalid
33341 * @param {String} msg The validation message
33343 markInvalid : function(msg)
33346 var label = this.el.select('label', true).first();
33347 var icon = this.el.select('i.fa-star', true).first();
33349 if(label && !icon){
33350 this.el.select('.roo-date-split-field-label', true).createChild({
33352 cls : 'text-danger fa fa-lg fa-star',
33353 tooltip : 'This field is required',
33354 style : 'margin-right:5px;'
33358 this.fireEvent('invalid', this, msg);
33361 clearInvalid : function()
33363 var label = this.el.select('label', true).first();
33364 var icon = this.el.select('i.fa-star', true).first();
33370 this.fireEvent('valid', this);
33373 getName: function()
33383 * http://masonry.desandro.com
33385 * The idea is to render all the bricks based on vertical width...
33387 * The original code extends 'outlayer' - we might need to use that....
33393 * @class Roo.bootstrap.LayoutMasonry
33394 * @extends Roo.bootstrap.Component
33395 * Bootstrap Layout Masonry class
33398 * Create a new Element
33399 * @param {Object} config The config object
33402 Roo.bootstrap.LayoutMasonry = function(config){
33404 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33408 Roo.bootstrap.LayoutMasonry.register(this);
33414 * Fire after layout the items
33415 * @param {Roo.bootstrap.LayoutMasonry} this
33416 * @param {Roo.EventObject} e
33423 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33426 * @cfg {Boolean} isLayoutInstant = no animation?
33428 isLayoutInstant : false, // needed?
33431 * @cfg {Number} boxWidth width of the columns
33436 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33441 * @cfg {Number} padWidth padding below box..
33446 * @cfg {Number} gutter gutter width..
33451 * @cfg {Number} maxCols maximum number of columns
33457 * @cfg {Boolean} isAutoInitial defalut true
33459 isAutoInitial : true,
33464 * @cfg {Boolean} isHorizontal defalut false
33466 isHorizontal : false,
33468 currentSize : null,
33474 bricks: null, //CompositeElement
33478 _isLayoutInited : false,
33480 // isAlternative : false, // only use for vertical layout...
33483 * @cfg {Number} alternativePadWidth padding below box..
33485 alternativePadWidth : 50,
33487 selectedBrick : [],
33489 getAutoCreate : function(){
33491 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33495 cls: 'blog-masonary-wrapper ' + this.cls,
33497 cls : 'mas-boxes masonary'
33504 getChildContainer: function( )
33506 if (this.boxesEl) {
33507 return this.boxesEl;
33510 this.boxesEl = this.el.select('.mas-boxes').first();
33512 return this.boxesEl;
33516 initEvents : function()
33520 if(this.isAutoInitial){
33521 Roo.log('hook children rendered');
33522 this.on('childrenrendered', function() {
33523 Roo.log('children rendered');
33529 initial : function()
33531 this.selectedBrick = [];
33533 this.currentSize = this.el.getBox(true);
33535 Roo.EventManager.onWindowResize(this.resize, this);
33537 if(!this.isAutoInitial){
33545 //this.layout.defer(500,this);
33549 resize : function()
33551 var cs = this.el.getBox(true);
33554 this.currentSize.width == cs.width &&
33555 this.currentSize.x == cs.x &&
33556 this.currentSize.height == cs.height &&
33557 this.currentSize.y == cs.y
33559 Roo.log("no change in with or X or Y");
33563 this.currentSize = cs;
33569 layout : function()
33571 this._resetLayout();
33573 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33575 this.layoutItems( isInstant );
33577 this._isLayoutInited = true;
33579 this.fireEvent('layout', this);
33583 _resetLayout : function()
33585 if(this.isHorizontal){
33586 this.horizontalMeasureColumns();
33590 this.verticalMeasureColumns();
33594 verticalMeasureColumns : function()
33596 this.getContainerWidth();
33598 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33599 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33603 var boxWidth = this.boxWidth + this.padWidth;
33605 if(this.containerWidth < this.boxWidth){
33606 boxWidth = this.containerWidth
33609 var containerWidth = this.containerWidth;
33611 var cols = Math.floor(containerWidth / boxWidth);
33613 this.cols = Math.max( cols, 1 );
33615 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33617 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33619 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33621 this.colWidth = boxWidth + avail - this.padWidth;
33623 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33624 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33627 horizontalMeasureColumns : function()
33629 this.getContainerWidth();
33631 var boxWidth = this.boxWidth;
33633 if(this.containerWidth < boxWidth){
33634 boxWidth = this.containerWidth;
33637 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33639 this.el.setHeight(boxWidth);
33643 getContainerWidth : function()
33645 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33648 layoutItems : function( isInstant )
33650 Roo.log(this.bricks);
33652 var items = Roo.apply([], this.bricks);
33654 if(this.isHorizontal){
33655 this._horizontalLayoutItems( items , isInstant );
33659 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33660 // this._verticalAlternativeLayoutItems( items , isInstant );
33664 this._verticalLayoutItems( items , isInstant );
33668 _verticalLayoutItems : function ( items , isInstant)
33670 if ( !items || !items.length ) {
33675 ['xs', 'xs', 'xs', 'tall'],
33676 ['xs', 'xs', 'tall'],
33677 ['xs', 'xs', 'sm'],
33678 ['xs', 'xs', 'xs'],
33684 ['sm', 'xs', 'xs'],
33688 ['tall', 'xs', 'xs', 'xs'],
33689 ['tall', 'xs', 'xs'],
33701 Roo.each(items, function(item, k){
33703 switch (item.size) {
33704 // these layouts take up a full box,
33715 boxes.push([item]);
33738 var filterPattern = function(box, length)
33746 var pattern = box.slice(0, length);
33750 Roo.each(pattern, function(i){
33751 format.push(i.size);
33754 Roo.each(standard, function(s){
33756 if(String(s) != String(format)){
33765 if(!match && length == 1){
33770 filterPattern(box, length - 1);
33774 queue.push(pattern);
33776 box = box.slice(length, box.length);
33778 filterPattern(box, 4);
33784 Roo.each(boxes, function(box, k){
33790 if(box.length == 1){
33795 filterPattern(box, 4);
33799 this._processVerticalLayoutQueue( queue, isInstant );
33803 // _verticalAlternativeLayoutItems : function( items , isInstant )
33805 // if ( !items || !items.length ) {
33809 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33813 _horizontalLayoutItems : function ( items , isInstant)
33815 if ( !items || !items.length || items.length < 3) {
33821 var eItems = items.slice(0, 3);
33823 items = items.slice(3, items.length);
33826 ['xs', 'xs', 'xs', 'wide'],
33827 ['xs', 'xs', 'wide'],
33828 ['xs', 'xs', 'sm'],
33829 ['xs', 'xs', 'xs'],
33835 ['sm', 'xs', 'xs'],
33839 ['wide', 'xs', 'xs', 'xs'],
33840 ['wide', 'xs', 'xs'],
33853 Roo.each(items, function(item, k){
33855 switch (item.size) {
33866 boxes.push([item]);
33890 var filterPattern = function(box, length)
33898 var pattern = box.slice(0, length);
33902 Roo.each(pattern, function(i){
33903 format.push(i.size);
33906 Roo.each(standard, function(s){
33908 if(String(s) != String(format)){
33917 if(!match && length == 1){
33922 filterPattern(box, length - 1);
33926 queue.push(pattern);
33928 box = box.slice(length, box.length);
33930 filterPattern(box, 4);
33936 Roo.each(boxes, function(box, k){
33942 if(box.length == 1){
33947 filterPattern(box, 4);
33954 var pos = this.el.getBox(true);
33958 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33960 var hit_end = false;
33962 Roo.each(queue, function(box){
33966 Roo.each(box, function(b){
33968 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33978 Roo.each(box, function(b){
33980 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33983 mx = Math.max(mx, b.x);
33987 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33991 Roo.each(box, function(b){
33993 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34007 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34010 /** Sets position of item in DOM
34011 * @param {Element} item
34012 * @param {Number} x - horizontal position
34013 * @param {Number} y - vertical position
34014 * @param {Boolean} isInstant - disables transitions
34016 _processVerticalLayoutQueue : function( queue, isInstant )
34018 var pos = this.el.getBox(true);
34023 for (var i = 0; i < this.cols; i++){
34027 Roo.each(queue, function(box, k){
34029 var col = k % this.cols;
34031 Roo.each(box, function(b,kk){
34033 b.el.position('absolute');
34035 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34036 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34038 if(b.size == 'md-left' || b.size == 'md-right'){
34039 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34040 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34043 b.el.setWidth(width);
34044 b.el.setHeight(height);
34046 b.el.select('iframe',true).setSize(width,height);
34050 for (var i = 0; i < this.cols; i++){
34052 if(maxY[i] < maxY[col]){
34057 col = Math.min(col, i);
34061 x = pos.x + col * (this.colWidth + this.padWidth);
34065 var positions = [];
34067 switch (box.length){
34069 positions = this.getVerticalOneBoxColPositions(x, y, box);
34072 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34075 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34078 positions = this.getVerticalFourBoxColPositions(x, y, box);
34084 Roo.each(box, function(b,kk){
34086 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34088 var sz = b.el.getSize();
34090 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34098 for (var i = 0; i < this.cols; i++){
34099 mY = Math.max(mY, maxY[i]);
34102 this.el.setHeight(mY - pos.y);
34106 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34108 // var pos = this.el.getBox(true);
34111 // var maxX = pos.right;
34113 // var maxHeight = 0;
34115 // Roo.each(items, function(item, k){
34119 // item.el.position('absolute');
34121 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34123 // item.el.setWidth(width);
34125 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34127 // item.el.setHeight(height);
34130 // item.el.setXY([x, y], isInstant ? false : true);
34132 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34135 // y = y + height + this.alternativePadWidth;
34137 // maxHeight = maxHeight + height + this.alternativePadWidth;
34141 // this.el.setHeight(maxHeight);
34145 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34147 var pos = this.el.getBox(true);
34152 var maxX = pos.right;
34154 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34156 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34158 Roo.each(queue, function(box, k){
34160 Roo.each(box, function(b, kk){
34162 b.el.position('absolute');
34164 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34165 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34167 if(b.size == 'md-left' || b.size == 'md-right'){
34168 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34169 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34172 b.el.setWidth(width);
34173 b.el.setHeight(height);
34181 var positions = [];
34183 switch (box.length){
34185 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34188 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34191 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34194 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34200 Roo.each(box, function(b,kk){
34202 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34204 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34212 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34214 Roo.each(eItems, function(b,k){
34216 b.size = (k == 0) ? 'sm' : 'xs';
34217 b.x = (k == 0) ? 2 : 1;
34218 b.y = (k == 0) ? 2 : 1;
34220 b.el.position('absolute');
34222 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34224 b.el.setWidth(width);
34226 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34228 b.el.setHeight(height);
34232 var positions = [];
34235 x : maxX - this.unitWidth * 2 - this.gutter,
34240 x : maxX - this.unitWidth,
34241 y : minY + (this.unitWidth + this.gutter) * 2
34245 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34249 Roo.each(eItems, function(b,k){
34251 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34257 getVerticalOneBoxColPositions : function(x, y, box)
34261 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34263 if(box[0].size == 'md-left'){
34267 if(box[0].size == 'md-right'){
34272 x : x + (this.unitWidth + this.gutter) * rand,
34279 getVerticalTwoBoxColPositions : function(x, y, box)
34283 if(box[0].size == 'xs'){
34287 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34291 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34305 x : x + (this.unitWidth + this.gutter) * 2,
34306 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34313 getVerticalThreeBoxColPositions : function(x, y, box)
34317 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34325 x : x + (this.unitWidth + this.gutter) * 1,
34330 x : x + (this.unitWidth + this.gutter) * 2,
34338 if(box[0].size == 'xs' && box[1].size == 'xs'){
34347 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34351 x : x + (this.unitWidth + this.gutter) * 1,
34365 x : x + (this.unitWidth + this.gutter) * 2,
34370 x : x + (this.unitWidth + this.gutter) * 2,
34371 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34378 getVerticalFourBoxColPositions : function(x, y, box)
34382 if(box[0].size == 'xs'){
34391 y : y + (this.unitHeight + this.gutter) * 1
34396 y : y + (this.unitHeight + this.gutter) * 2
34400 x : x + (this.unitWidth + this.gutter) * 1,
34414 x : x + (this.unitWidth + this.gutter) * 2,
34419 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34420 y : y + (this.unitHeight + this.gutter) * 1
34424 x : x + (this.unitWidth + this.gutter) * 2,
34425 y : y + (this.unitWidth + this.gutter) * 2
34432 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34436 if(box[0].size == 'md-left'){
34438 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34445 if(box[0].size == 'md-right'){
34447 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34448 y : minY + (this.unitWidth + this.gutter) * 1
34454 var rand = Math.floor(Math.random() * (4 - box[0].y));
34457 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34458 y : minY + (this.unitWidth + this.gutter) * rand
34465 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34469 if(box[0].size == 'xs'){
34472 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34477 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34478 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34486 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34491 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34492 y : minY + (this.unitWidth + this.gutter) * 2
34499 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34503 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34506 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34511 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34512 y : minY + (this.unitWidth + this.gutter) * 1
34516 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34517 y : minY + (this.unitWidth + this.gutter) * 2
34524 if(box[0].size == 'xs' && box[1].size == 'xs'){
34527 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34532 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34537 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34538 y : minY + (this.unitWidth + this.gutter) * 1
34546 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34551 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34552 y : minY + (this.unitWidth + this.gutter) * 2
34556 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34557 y : minY + (this.unitWidth + this.gutter) * 2
34564 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34568 if(box[0].size == 'xs'){
34571 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34576 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34581 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),
34586 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34587 y : minY + (this.unitWidth + this.gutter) * 1
34595 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34600 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34601 y : minY + (this.unitWidth + this.gutter) * 2
34605 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34606 y : minY + (this.unitWidth + this.gutter) * 2
34610 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),
34611 y : minY + (this.unitWidth + this.gutter) * 2
34619 * remove a Masonry Brick
34620 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34622 removeBrick : function(brick_id)
34628 for (var i = 0; i<this.bricks.length; i++) {
34629 if (this.bricks[i].id == brick_id) {
34630 this.bricks.splice(i,1);
34631 this.el.dom.removeChild(Roo.get(brick_id).dom);
34638 * adds a Masonry Brick
34639 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34641 addBrick : function(cfg)
34643 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34644 //this.register(cn);
34645 cn.parentId = this.id;
34646 cn.render(this.el);
34651 * register a Masonry Brick
34652 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34655 register : function(brick)
34657 this.bricks.push(brick);
34658 brick.masonryId = this.id;
34662 * clear all the Masonry Brick
34664 clearAll : function()
34667 //this.getChildContainer().dom.innerHTML = "";
34668 this.el.dom.innerHTML = '';
34671 getSelected : function()
34673 if (!this.selectedBrick) {
34677 return this.selectedBrick;
34681 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34685 * register a Masonry Layout
34686 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34689 register : function(layout)
34691 this.groups[layout.id] = layout;
34694 * fetch a Masonry Layout based on the masonry layout ID
34695 * @param {string} the masonry layout to add
34696 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34699 get: function(layout_id) {
34700 if (typeof(this.groups[layout_id]) == 'undefined') {
34703 return this.groups[layout_id] ;
34715 * http://masonry.desandro.com
34717 * The idea is to render all the bricks based on vertical width...
34719 * The original code extends 'outlayer' - we might need to use that....
34725 * @class Roo.bootstrap.LayoutMasonryAuto
34726 * @extends Roo.bootstrap.Component
34727 * Bootstrap Layout Masonry class
34730 * Create a new Element
34731 * @param {Object} config The config object
34734 Roo.bootstrap.LayoutMasonryAuto = function(config){
34735 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34738 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34741 * @cfg {Boolean} isFitWidth - resize the width..
34743 isFitWidth : false, // options..
34745 * @cfg {Boolean} isOriginLeft = left align?
34747 isOriginLeft : true,
34749 * @cfg {Boolean} isOriginTop = top align?
34751 isOriginTop : false,
34753 * @cfg {Boolean} isLayoutInstant = no animation?
34755 isLayoutInstant : false, // needed?
34757 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34759 isResizingContainer : true,
34761 * @cfg {Number} columnWidth width of the columns
34767 * @cfg {Number} maxCols maximum number of columns
34772 * @cfg {Number} padHeight padding below box..
34778 * @cfg {Boolean} isAutoInitial defalut true
34781 isAutoInitial : true,
34787 initialColumnWidth : 0,
34788 currentSize : null,
34790 colYs : null, // array.
34797 bricks: null, //CompositeElement
34798 cols : 0, // array?
34799 // element : null, // wrapped now this.el
34800 _isLayoutInited : null,
34803 getAutoCreate : function(){
34807 cls: 'blog-masonary-wrapper ' + this.cls,
34809 cls : 'mas-boxes masonary'
34816 getChildContainer: function( )
34818 if (this.boxesEl) {
34819 return this.boxesEl;
34822 this.boxesEl = this.el.select('.mas-boxes').first();
34824 return this.boxesEl;
34828 initEvents : function()
34832 if(this.isAutoInitial){
34833 Roo.log('hook children rendered');
34834 this.on('childrenrendered', function() {
34835 Roo.log('children rendered');
34842 initial : function()
34844 this.reloadItems();
34846 this.currentSize = this.el.getBox(true);
34848 /// was window resize... - let's see if this works..
34849 Roo.EventManager.onWindowResize(this.resize, this);
34851 if(!this.isAutoInitial){
34856 this.layout.defer(500,this);
34859 reloadItems: function()
34861 this.bricks = this.el.select('.masonry-brick', true);
34863 this.bricks.each(function(b) {
34864 //Roo.log(b.getSize());
34865 if (!b.attr('originalwidth')) {
34866 b.attr('originalwidth', b.getSize().width);
34871 Roo.log(this.bricks.elements.length);
34874 resize : function()
34877 var cs = this.el.getBox(true);
34879 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34880 Roo.log("no change in with or X");
34883 this.currentSize = cs;
34887 layout : function()
34890 this._resetLayout();
34891 //this._manageStamps();
34893 // don't animate first layout
34894 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34895 this.layoutItems( isInstant );
34897 // flag for initalized
34898 this._isLayoutInited = true;
34901 layoutItems : function( isInstant )
34903 //var items = this._getItemsForLayout( this.items );
34904 // original code supports filtering layout items.. we just ignore it..
34906 this._layoutItems( this.bricks , isInstant );
34908 this._postLayout();
34910 _layoutItems : function ( items , isInstant)
34912 //this.fireEvent( 'layout', this, items );
34915 if ( !items || !items.elements.length ) {
34916 // no items, emit event with empty array
34921 items.each(function(item) {
34922 Roo.log("layout item");
34924 // get x/y object from method
34925 var position = this._getItemLayoutPosition( item );
34927 position.item = item;
34928 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34929 queue.push( position );
34932 this._processLayoutQueue( queue );
34934 /** Sets position of item in DOM
34935 * @param {Element} item
34936 * @param {Number} x - horizontal position
34937 * @param {Number} y - vertical position
34938 * @param {Boolean} isInstant - disables transitions
34940 _processLayoutQueue : function( queue )
34942 for ( var i=0, len = queue.length; i < len; i++ ) {
34943 var obj = queue[i];
34944 obj.item.position('absolute');
34945 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34951 * Any logic you want to do after each layout,
34952 * i.e. size the container
34954 _postLayout : function()
34956 this.resizeContainer();
34959 resizeContainer : function()
34961 if ( !this.isResizingContainer ) {
34964 var size = this._getContainerSize();
34966 this.el.setSize(size.width,size.height);
34967 this.boxesEl.setSize(size.width,size.height);
34973 _resetLayout : function()
34975 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34976 this.colWidth = this.el.getWidth();
34977 //this.gutter = this.el.getWidth();
34979 this.measureColumns();
34985 this.colYs.push( 0 );
34991 measureColumns : function()
34993 this.getContainerWidth();
34994 // if columnWidth is 0, default to outerWidth of first item
34995 if ( !this.columnWidth ) {
34996 var firstItem = this.bricks.first();
34997 Roo.log(firstItem);
34998 this.columnWidth = this.containerWidth;
34999 if (firstItem && firstItem.attr('originalwidth') ) {
35000 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35002 // columnWidth fall back to item of first element
35003 Roo.log("set column width?");
35004 this.initialColumnWidth = this.columnWidth ;
35006 // if first elem has no width, default to size of container
35011 if (this.initialColumnWidth) {
35012 this.columnWidth = this.initialColumnWidth;
35017 // column width is fixed at the top - however if container width get's smaller we should
35020 // this bit calcs how man columns..
35022 var columnWidth = this.columnWidth += this.gutter;
35024 // calculate columns
35025 var containerWidth = this.containerWidth + this.gutter;
35027 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35028 // fix rounding errors, typically with gutters
35029 var excess = columnWidth - containerWidth % columnWidth;
35032 // if overshoot is less than a pixel, round up, otherwise floor it
35033 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35034 cols = Math[ mathMethod ]( cols );
35035 this.cols = Math.max( cols, 1 );
35036 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35038 // padding positioning..
35039 var totalColWidth = this.cols * this.columnWidth;
35040 var padavail = this.containerWidth - totalColWidth;
35041 // so for 2 columns - we need 3 'pads'
35043 var padNeeded = (1+this.cols) * this.padWidth;
35045 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35047 this.columnWidth += padExtra
35048 //this.padWidth = Math.floor(padavail / ( this.cols));
35050 // adjust colum width so that padding is fixed??
35052 // we have 3 columns ... total = width * 3
35053 // we have X left over... that should be used by
35055 //if (this.expandC) {
35063 getContainerWidth : function()
35065 /* // container is parent if fit width
35066 var container = this.isFitWidth ? this.element.parentNode : this.element;
35067 // check that this.size and size are there
35068 // IE8 triggers resize on body size change, so they might not be
35070 var size = getSize( container ); //FIXME
35071 this.containerWidth = size && size.innerWidth; //FIXME
35074 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35078 _getItemLayoutPosition : function( item ) // what is item?
35080 // we resize the item to our columnWidth..
35082 item.setWidth(this.columnWidth);
35083 item.autoBoxAdjust = false;
35085 var sz = item.getSize();
35087 // how many columns does this brick span
35088 var remainder = this.containerWidth % this.columnWidth;
35090 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35091 // round if off by 1 pixel, otherwise use ceil
35092 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35093 colSpan = Math.min( colSpan, this.cols );
35095 // normally this should be '1' as we dont' currently allow multi width columns..
35097 var colGroup = this._getColGroup( colSpan );
35098 // get the minimum Y value from the columns
35099 var minimumY = Math.min.apply( Math, colGroup );
35100 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35102 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35104 // position the brick
35106 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35107 y: this.currentSize.y + minimumY + this.padHeight
35111 // apply setHeight to necessary columns
35112 var setHeight = minimumY + sz.height + this.padHeight;
35113 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35115 var setSpan = this.cols + 1 - colGroup.length;
35116 for ( var i = 0; i < setSpan; i++ ) {
35117 this.colYs[ shortColIndex + i ] = setHeight ;
35124 * @param {Number} colSpan - number of columns the element spans
35125 * @returns {Array} colGroup
35127 _getColGroup : function( colSpan )
35129 if ( colSpan < 2 ) {
35130 // if brick spans only one column, use all the column Ys
35135 // how many different places could this brick fit horizontally
35136 var groupCount = this.cols + 1 - colSpan;
35137 // for each group potential horizontal position
35138 for ( var i = 0; i < groupCount; i++ ) {
35139 // make an array of colY values for that one group
35140 var groupColYs = this.colYs.slice( i, i + colSpan );
35141 // and get the max value of the array
35142 colGroup[i] = Math.max.apply( Math, groupColYs );
35147 _manageStamp : function( stamp )
35149 var stampSize = stamp.getSize();
35150 var offset = stamp.getBox();
35151 // get the columns that this stamp affects
35152 var firstX = this.isOriginLeft ? offset.x : offset.right;
35153 var lastX = firstX + stampSize.width;
35154 var firstCol = Math.floor( firstX / this.columnWidth );
35155 firstCol = Math.max( 0, firstCol );
35157 var lastCol = Math.floor( lastX / this.columnWidth );
35158 // lastCol should not go over if multiple of columnWidth #425
35159 lastCol -= lastX % this.columnWidth ? 0 : 1;
35160 lastCol = Math.min( this.cols - 1, lastCol );
35162 // set colYs to bottom of the stamp
35163 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35166 for ( var i = firstCol; i <= lastCol; i++ ) {
35167 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35172 _getContainerSize : function()
35174 this.maxY = Math.max.apply( Math, this.colYs );
35179 if ( this.isFitWidth ) {
35180 size.width = this._getContainerFitWidth();
35186 _getContainerFitWidth : function()
35188 var unusedCols = 0;
35189 // count unused columns
35192 if ( this.colYs[i] !== 0 ) {
35197 // fit container to columns that have been used
35198 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35201 needsResizeLayout : function()
35203 var previousWidth = this.containerWidth;
35204 this.getContainerWidth();
35205 return previousWidth !== this.containerWidth;
35220 * @class Roo.bootstrap.MasonryBrick
35221 * @extends Roo.bootstrap.Component
35222 * Bootstrap MasonryBrick class
35225 * Create a new MasonryBrick
35226 * @param {Object} config The config object
35229 Roo.bootstrap.MasonryBrick = function(config){
35231 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35233 Roo.bootstrap.MasonryBrick.register(this);
35239 * When a MasonryBrick is clcik
35240 * @param {Roo.bootstrap.MasonryBrick} this
35241 * @param {Roo.EventObject} e
35247 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35250 * @cfg {String} title
35254 * @cfg {String} html
35258 * @cfg {String} bgimage
35262 * @cfg {String} videourl
35266 * @cfg {String} cls
35270 * @cfg {String} href
35274 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35279 * @cfg {String} placetitle (center|bottom)
35284 * @cfg {Boolean} isFitContainer defalut true
35286 isFitContainer : true,
35289 * @cfg {Boolean} preventDefault defalut false
35291 preventDefault : false,
35294 * @cfg {Boolean} inverse defalut false
35296 maskInverse : false,
35298 getAutoCreate : function()
35300 if(!this.isFitContainer){
35301 return this.getSplitAutoCreate();
35304 var cls = 'masonry-brick masonry-brick-full';
35306 if(this.href.length){
35307 cls += ' masonry-brick-link';
35310 if(this.bgimage.length){
35311 cls += ' masonry-brick-image';
35314 if(this.maskInverse){
35315 cls += ' mask-inverse';
35318 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35319 cls += ' enable-mask';
35323 cls += ' masonry-' + this.size + '-brick';
35326 if(this.placetitle.length){
35328 switch (this.placetitle) {
35330 cls += ' masonry-center-title';
35333 cls += ' masonry-bottom-title';
35340 if(!this.html.length && !this.bgimage.length){
35341 cls += ' masonry-center-title';
35344 if(!this.html.length && this.bgimage.length){
35345 cls += ' masonry-bottom-title';
35350 cls += ' ' + this.cls;
35354 tag: (this.href.length) ? 'a' : 'div',
35359 cls: 'masonry-brick-mask'
35363 cls: 'masonry-brick-paragraph',
35369 if(this.href.length){
35370 cfg.href = this.href;
35373 var cn = cfg.cn[1].cn;
35375 if(this.title.length){
35378 cls: 'masonry-brick-title',
35383 if(this.html.length){
35386 cls: 'masonry-brick-text',
35391 if (!this.title.length && !this.html.length) {
35392 cfg.cn[1].cls += ' hide';
35395 if(this.bgimage.length){
35398 cls: 'masonry-brick-image-view',
35403 if(this.videourl.length){
35404 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35405 // youtube support only?
35408 cls: 'masonry-brick-image-view',
35411 allowfullscreen : true
35419 getSplitAutoCreate : function()
35421 var cls = 'masonry-brick masonry-brick-split';
35423 if(this.href.length){
35424 cls += ' masonry-brick-link';
35427 if(this.bgimage.length){
35428 cls += ' masonry-brick-image';
35432 cls += ' masonry-' + this.size + '-brick';
35435 switch (this.placetitle) {
35437 cls += ' masonry-center-title';
35440 cls += ' masonry-bottom-title';
35443 if(!this.bgimage.length){
35444 cls += ' masonry-center-title';
35447 if(this.bgimage.length){
35448 cls += ' masonry-bottom-title';
35454 cls += ' ' + this.cls;
35458 tag: (this.href.length) ? 'a' : 'div',
35463 cls: 'masonry-brick-split-head',
35467 cls: 'masonry-brick-paragraph',
35474 cls: 'masonry-brick-split-body',
35480 if(this.href.length){
35481 cfg.href = this.href;
35484 if(this.title.length){
35485 cfg.cn[0].cn[0].cn.push({
35487 cls: 'masonry-brick-title',
35492 if(this.html.length){
35493 cfg.cn[1].cn.push({
35495 cls: 'masonry-brick-text',
35500 if(this.bgimage.length){
35501 cfg.cn[0].cn.push({
35503 cls: 'masonry-brick-image-view',
35508 if(this.videourl.length){
35509 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35510 // youtube support only?
35511 cfg.cn[0].cn.cn.push({
35513 cls: 'masonry-brick-image-view',
35516 allowfullscreen : true
35523 initEvents: function()
35525 switch (this.size) {
35558 this.el.on('touchstart', this.onTouchStart, this);
35559 this.el.on('touchmove', this.onTouchMove, this);
35560 this.el.on('touchend', this.onTouchEnd, this);
35561 this.el.on('contextmenu', this.onContextMenu, this);
35563 this.el.on('mouseenter' ,this.enter, this);
35564 this.el.on('mouseleave', this.leave, this);
35565 this.el.on('click', this.onClick, this);
35568 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35569 this.parent().bricks.push(this);
35574 onClick: function(e, el)
35576 var time = this.endTimer - this.startTimer;
35577 // Roo.log(e.preventDefault());
35580 e.preventDefault();
35585 if(!this.preventDefault){
35589 e.preventDefault();
35591 if (this.activeClass != '') {
35592 this.selectBrick();
35595 this.fireEvent('click', this, e);
35598 enter: function(e, el)
35600 e.preventDefault();
35602 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35606 if(this.bgimage.length && this.html.length){
35607 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35611 leave: function(e, el)
35613 e.preventDefault();
35615 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35619 if(this.bgimage.length && this.html.length){
35620 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35624 onTouchStart: function(e, el)
35626 // e.preventDefault();
35628 this.touchmoved = false;
35630 if(!this.isFitContainer){
35634 if(!this.bgimage.length || !this.html.length){
35638 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35640 this.timer = new Date().getTime();
35644 onTouchMove: function(e, el)
35646 this.touchmoved = true;
35649 onContextMenu : function(e,el)
35651 e.preventDefault();
35652 e.stopPropagation();
35656 onTouchEnd: function(e, el)
35658 // e.preventDefault();
35660 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35667 if(!this.bgimage.length || !this.html.length){
35669 if(this.href.length){
35670 window.location.href = this.href;
35676 if(!this.isFitContainer){
35680 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35682 window.location.href = this.href;
35685 //selection on single brick only
35686 selectBrick : function() {
35688 if (!this.parentId) {
35692 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35693 var index = m.selectedBrick.indexOf(this.id);
35696 m.selectedBrick.splice(index,1);
35697 this.el.removeClass(this.activeClass);
35701 for(var i = 0; i < m.selectedBrick.length; i++) {
35702 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35703 b.el.removeClass(b.activeClass);
35706 m.selectedBrick = [];
35708 m.selectedBrick.push(this.id);
35709 this.el.addClass(this.activeClass);
35713 isSelected : function(){
35714 return this.el.hasClass(this.activeClass);
35719 Roo.apply(Roo.bootstrap.MasonryBrick, {
35722 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35724 * register a Masonry Brick
35725 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35728 register : function(brick)
35730 //this.groups[brick.id] = brick;
35731 this.groups.add(brick.id, brick);
35734 * fetch a masonry brick based on the masonry brick ID
35735 * @param {string} the masonry brick to add
35736 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35739 get: function(brick_id)
35741 // if (typeof(this.groups[brick_id]) == 'undefined') {
35744 // return this.groups[brick_id] ;
35746 if(this.groups.key(brick_id)) {
35747 return this.groups.key(brick_id);
35765 * @class Roo.bootstrap.Brick
35766 * @extends Roo.bootstrap.Component
35767 * Bootstrap Brick class
35770 * Create a new Brick
35771 * @param {Object} config The config object
35774 Roo.bootstrap.Brick = function(config){
35775 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35781 * When a Brick is click
35782 * @param {Roo.bootstrap.Brick} this
35783 * @param {Roo.EventObject} e
35789 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35792 * @cfg {String} title
35796 * @cfg {String} html
35800 * @cfg {String} bgimage
35804 * @cfg {String} cls
35808 * @cfg {String} href
35812 * @cfg {String} video
35816 * @cfg {Boolean} square
35820 getAutoCreate : function()
35822 var cls = 'roo-brick';
35824 if(this.href.length){
35825 cls += ' roo-brick-link';
35828 if(this.bgimage.length){
35829 cls += ' roo-brick-image';
35832 if(!this.html.length && !this.bgimage.length){
35833 cls += ' roo-brick-center-title';
35836 if(!this.html.length && this.bgimage.length){
35837 cls += ' roo-brick-bottom-title';
35841 cls += ' ' + this.cls;
35845 tag: (this.href.length) ? 'a' : 'div',
35850 cls: 'roo-brick-paragraph',
35856 if(this.href.length){
35857 cfg.href = this.href;
35860 var cn = cfg.cn[0].cn;
35862 if(this.title.length){
35865 cls: 'roo-brick-title',
35870 if(this.html.length){
35873 cls: 'roo-brick-text',
35880 if(this.bgimage.length){
35883 cls: 'roo-brick-image-view',
35891 initEvents: function()
35893 if(this.title.length || this.html.length){
35894 this.el.on('mouseenter' ,this.enter, this);
35895 this.el.on('mouseleave', this.leave, this);
35898 Roo.EventManager.onWindowResize(this.resize, this);
35900 if(this.bgimage.length){
35901 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35902 this.imageEl.on('load', this.onImageLoad, this);
35909 onImageLoad : function()
35914 resize : function()
35916 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35918 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35920 if(this.bgimage.length){
35921 var image = this.el.select('.roo-brick-image-view', true).first();
35923 image.setWidth(paragraph.getWidth());
35926 image.setHeight(paragraph.getWidth());
35929 this.el.setHeight(image.getHeight());
35930 paragraph.setHeight(image.getHeight());
35936 enter: function(e, el)
35938 e.preventDefault();
35940 if(this.bgimage.length){
35941 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35942 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35946 leave: function(e, el)
35948 e.preventDefault();
35950 if(this.bgimage.length){
35951 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35952 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35967 * @class Roo.bootstrap.NumberField
35968 * @extends Roo.bootstrap.Input
35969 * Bootstrap NumberField class
35975 * Create a new NumberField
35976 * @param {Object} config The config object
35979 Roo.bootstrap.NumberField = function(config){
35980 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35983 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35986 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35988 allowDecimals : true,
35990 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35992 decimalSeparator : ".",
35994 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35996 decimalPrecision : 2,
35998 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36000 allowNegative : true,
36003 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36007 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36009 minValue : Number.NEGATIVE_INFINITY,
36011 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36013 maxValue : Number.MAX_VALUE,
36015 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36017 minText : "The minimum value for this field is {0}",
36019 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36021 maxText : "The maximum value for this field is {0}",
36023 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36024 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36026 nanText : "{0} is not a valid number",
36028 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36030 thousandsDelimiter : false,
36032 * @cfg {String} valueAlign alignment of value
36034 valueAlign : "left",
36036 getAutoCreate : function()
36038 var hiddenInput = {
36042 cls: 'hidden-number-input'
36046 hiddenInput.name = this.name;
36051 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36053 this.name = hiddenInput.name;
36055 if(cfg.cn.length > 0) {
36056 cfg.cn.push(hiddenInput);
36063 initEvents : function()
36065 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36067 var allowed = "0123456789";
36069 if(this.allowDecimals){
36070 allowed += this.decimalSeparator;
36073 if(this.allowNegative){
36077 if(this.thousandsDelimiter) {
36081 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36083 var keyPress = function(e){
36085 var k = e.getKey();
36087 var c = e.getCharCode();
36090 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36091 allowed.indexOf(String.fromCharCode(c)) === -1
36097 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36101 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36106 this.el.on("keypress", keyPress, this);
36109 validateValue : function(value)
36112 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36116 var num = this.parseValue(value);
36119 this.markInvalid(String.format(this.nanText, value));
36123 if(num < this.minValue){
36124 this.markInvalid(String.format(this.minText, this.minValue));
36128 if(num > this.maxValue){
36129 this.markInvalid(String.format(this.maxText, this.maxValue));
36136 getValue : function()
36138 var v = this.hiddenEl().getValue();
36140 return this.fixPrecision(this.parseValue(v));
36143 parseValue : function(value)
36145 if(this.thousandsDelimiter) {
36147 r = new RegExp(",", "g");
36148 value = value.replace(r, "");
36151 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36152 return isNaN(value) ? '' : value;
36155 fixPrecision : function(value)
36157 if(this.thousandsDelimiter) {
36159 r = new RegExp(",", "g");
36160 value = value.replace(r, "");
36163 var nan = isNaN(value);
36165 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36166 return nan ? '' : value;
36168 return parseFloat(value).toFixed(this.decimalPrecision);
36171 setValue : function(v)
36173 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36179 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36181 this.inputEl().dom.value = (v == '') ? '' :
36182 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36184 if(!this.allowZero && v === '0') {
36185 this.hiddenEl().dom.value = '';
36186 this.inputEl().dom.value = '';
36193 decimalPrecisionFcn : function(v)
36195 return Math.floor(v);
36198 beforeBlur : function()
36200 var v = this.parseValue(this.getRawValue());
36202 if(v || v === 0 || v === ''){
36207 hiddenEl : function()
36209 return this.el.select('input.hidden-number-input',true).first();
36221 * @class Roo.bootstrap.DocumentSlider
36222 * @extends Roo.bootstrap.Component
36223 * Bootstrap DocumentSlider class
36226 * Create a new DocumentViewer
36227 * @param {Object} config The config object
36230 Roo.bootstrap.DocumentSlider = function(config){
36231 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36238 * Fire after initEvent
36239 * @param {Roo.bootstrap.DocumentSlider} this
36244 * Fire after update
36245 * @param {Roo.bootstrap.DocumentSlider} this
36251 * @param {Roo.bootstrap.DocumentSlider} this
36257 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36263 getAutoCreate : function()
36267 cls : 'roo-document-slider',
36271 cls : 'roo-document-slider-header',
36275 cls : 'roo-document-slider-header-title'
36281 cls : 'roo-document-slider-body',
36285 cls : 'roo-document-slider-prev',
36289 cls : 'fa fa-chevron-left'
36295 cls : 'roo-document-slider-thumb',
36299 cls : 'roo-document-slider-image'
36305 cls : 'roo-document-slider-next',
36309 cls : 'fa fa-chevron-right'
36321 initEvents : function()
36323 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36324 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36326 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36327 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36329 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36330 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36332 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36333 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36335 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36336 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36338 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36339 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36341 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36342 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36344 this.thumbEl.on('click', this.onClick, this);
36346 this.prevIndicator.on('click', this.prev, this);
36348 this.nextIndicator.on('click', this.next, this);
36352 initial : function()
36354 if(this.files.length){
36355 this.indicator = 1;
36359 this.fireEvent('initial', this);
36362 update : function()
36364 this.imageEl.attr('src', this.files[this.indicator - 1]);
36366 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36368 this.prevIndicator.show();
36370 if(this.indicator == 1){
36371 this.prevIndicator.hide();
36374 this.nextIndicator.show();
36376 if(this.indicator == this.files.length){
36377 this.nextIndicator.hide();
36380 this.thumbEl.scrollTo('top');
36382 this.fireEvent('update', this);
36385 onClick : function(e)
36387 e.preventDefault();
36389 this.fireEvent('click', this);
36394 e.preventDefault();
36396 this.indicator = Math.max(1, this.indicator - 1);
36403 e.preventDefault();
36405 this.indicator = Math.min(this.files.length, this.indicator + 1);
36419 * @class Roo.bootstrap.RadioSet
36420 * @extends Roo.bootstrap.Input
36421 * Bootstrap RadioSet class
36422 * @cfg {String} indicatorpos (left|right) default left
36423 * @cfg {Boolean} inline (true|false) inline the element (default true)
36424 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36426 * Create a new RadioSet
36427 * @param {Object} config The config object
36430 Roo.bootstrap.RadioSet = function(config){
36432 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36436 Roo.bootstrap.RadioSet.register(this);
36441 * Fires when the element is checked or unchecked.
36442 * @param {Roo.bootstrap.RadioSet} this This radio
36443 * @param {Roo.bootstrap.Radio} item The checked item
36448 * Fires when the element is click.
36449 * @param {Roo.bootstrap.RadioSet} this This radio set
36450 * @param {Roo.bootstrap.Radio} item The checked item
36451 * @param {Roo.EventObject} e The event object
36458 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36466 indicatorpos : 'left',
36468 getAutoCreate : function()
36472 cls : 'roo-radio-set-label',
36476 html : this.fieldLabel
36480 if (Roo.bootstrap.version == 3) {
36483 if(this.indicatorpos == 'left'){
36486 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36487 tooltip : 'This field is required'
36492 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36493 tooltip : 'This field is required'
36499 cls : 'roo-radio-set-items'
36502 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36504 if (align === 'left' && this.fieldLabel.length) {
36507 cls : "roo-radio-set-right",
36513 if(this.labelWidth > 12){
36514 label.style = "width: " + this.labelWidth + 'px';
36517 if(this.labelWidth < 13 && this.labelmd == 0){
36518 this.labelmd = this.labelWidth;
36521 if(this.labellg > 0){
36522 label.cls += ' col-lg-' + this.labellg;
36523 items.cls += ' col-lg-' + (12 - this.labellg);
36526 if(this.labelmd > 0){
36527 label.cls += ' col-md-' + this.labelmd;
36528 items.cls += ' col-md-' + (12 - this.labelmd);
36531 if(this.labelsm > 0){
36532 label.cls += ' col-sm-' + this.labelsm;
36533 items.cls += ' col-sm-' + (12 - this.labelsm);
36536 if(this.labelxs > 0){
36537 label.cls += ' col-xs-' + this.labelxs;
36538 items.cls += ' col-xs-' + (12 - this.labelxs);
36544 cls : 'roo-radio-set',
36548 cls : 'roo-radio-set-input',
36551 value : this.value ? this.value : ''
36558 if(this.weight.length){
36559 cfg.cls += ' roo-radio-' + this.weight;
36563 cfg.cls += ' roo-radio-set-inline';
36567 ['xs','sm','md','lg'].map(function(size){
36568 if (settings[size]) {
36569 cfg.cls += ' col-' + size + '-' + settings[size];
36577 initEvents : function()
36579 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36580 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36582 if(!this.fieldLabel.length){
36583 this.labelEl.hide();
36586 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36587 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36589 this.indicator = this.indicatorEl();
36591 if(this.indicator){
36592 this.indicator.addClass('invisible');
36595 this.originalValue = this.getValue();
36599 inputEl: function ()
36601 return this.el.select('.roo-radio-set-input', true).first();
36604 getChildContainer : function()
36606 return this.itemsEl;
36609 register : function(item)
36611 this.radioes.push(item);
36615 validate : function()
36617 if(this.getVisibilityEl().hasClass('hidden')){
36623 Roo.each(this.radioes, function(i){
36632 if(this.allowBlank) {
36636 if(this.disabled || valid){
36641 this.markInvalid();
36646 markValid : function()
36648 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36649 this.indicatorEl().removeClass('visible');
36650 this.indicatorEl().addClass('invisible');
36654 if (Roo.bootstrap.version == 3) {
36655 this.el.removeClass([this.invalidClass, this.validClass]);
36656 this.el.addClass(this.validClass);
36658 this.el.removeClass(['is-invalid','is-valid']);
36659 this.el.addClass(['is-valid']);
36661 this.fireEvent('valid', this);
36664 markInvalid : function(msg)
36666 if(this.allowBlank || this.disabled){
36670 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36671 this.indicatorEl().removeClass('invisible');
36672 this.indicatorEl().addClass('visible');
36674 if (Roo.bootstrap.version == 3) {
36675 this.el.removeClass([this.invalidClass, this.validClass]);
36676 this.el.addClass(this.invalidClass);
36678 this.el.removeClass(['is-invalid','is-valid']);
36679 this.el.addClass(['is-invalid']);
36682 this.fireEvent('invalid', this, msg);
36686 setValue : function(v, suppressEvent)
36688 if(this.value === v){
36695 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36698 Roo.each(this.radioes, function(i){
36700 i.el.removeClass('checked');
36703 Roo.each(this.radioes, function(i){
36705 if(i.value === v || i.value.toString() === v.toString()){
36707 i.el.addClass('checked');
36709 if(suppressEvent !== true){
36710 this.fireEvent('check', this, i);
36721 clearInvalid : function(){
36723 if(!this.el || this.preventMark){
36727 this.el.removeClass([this.invalidClass]);
36729 this.fireEvent('valid', this);
36734 Roo.apply(Roo.bootstrap.RadioSet, {
36738 register : function(set)
36740 this.groups[set.name] = set;
36743 get: function(name)
36745 if (typeof(this.groups[name]) == 'undefined') {
36749 return this.groups[name] ;
36755 * Ext JS Library 1.1.1
36756 * Copyright(c) 2006-2007, Ext JS, LLC.
36758 * Originally Released Under LGPL - original licence link has changed is not relivant.
36761 * <script type="text/javascript">
36766 * @class Roo.bootstrap.SplitBar
36767 * @extends Roo.util.Observable
36768 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36772 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36773 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36774 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36775 split.minSize = 100;
36776 split.maxSize = 600;
36777 split.animate = true;
36778 split.on('moved', splitterMoved);
36781 * Create a new SplitBar
36782 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36783 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36784 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36785 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36786 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36787 position of the SplitBar).
36789 Roo.bootstrap.SplitBar = function(cfg){
36794 // dragElement : elm
36795 // resizingElement: el,
36797 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36798 // placement : Roo.bootstrap.SplitBar.LEFT ,
36799 // existingProxy ???
36802 this.el = Roo.get(cfg.dragElement, true);
36803 this.el.dom.unselectable = "on";
36805 this.resizingEl = Roo.get(cfg.resizingElement, true);
36809 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36810 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36813 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36816 * The minimum size of the resizing element. (Defaults to 0)
36822 * The maximum size of the resizing element. (Defaults to 2000)
36825 this.maxSize = 2000;
36828 * Whether to animate the transition to the new size
36831 this.animate = false;
36834 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36837 this.useShim = false;
36842 if(!cfg.existingProxy){
36844 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36846 this.proxy = Roo.get(cfg.existingProxy).dom;
36849 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36852 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36855 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36858 this.dragSpecs = {};
36861 * @private The adapter to use to positon and resize elements
36863 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36864 this.adapter.init(this);
36866 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36868 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36869 this.el.addClass("roo-splitbar-h");
36872 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36873 this.el.addClass("roo-splitbar-v");
36879 * Fires when the splitter is moved (alias for {@link #event-moved})
36880 * @param {Roo.bootstrap.SplitBar} this
36881 * @param {Number} newSize the new width or height
36886 * Fires when the splitter is moved
36887 * @param {Roo.bootstrap.SplitBar} this
36888 * @param {Number} newSize the new width or height
36892 * @event beforeresize
36893 * Fires before the splitter is dragged
36894 * @param {Roo.bootstrap.SplitBar} this
36896 "beforeresize" : true,
36898 "beforeapply" : true
36901 Roo.util.Observable.call(this);
36904 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36905 onStartProxyDrag : function(x, y){
36906 this.fireEvent("beforeresize", this);
36908 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36910 o.enableDisplayMode("block");
36911 // all splitbars share the same overlay
36912 Roo.bootstrap.SplitBar.prototype.overlay = o;
36914 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36915 this.overlay.show();
36916 Roo.get(this.proxy).setDisplayed("block");
36917 var size = this.adapter.getElementSize(this);
36918 this.activeMinSize = this.getMinimumSize();;
36919 this.activeMaxSize = this.getMaximumSize();;
36920 var c1 = size - this.activeMinSize;
36921 var c2 = Math.max(this.activeMaxSize - size, 0);
36922 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36923 this.dd.resetConstraints();
36924 this.dd.setXConstraint(
36925 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36926 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36928 this.dd.setYConstraint(0, 0);
36930 this.dd.resetConstraints();
36931 this.dd.setXConstraint(0, 0);
36932 this.dd.setYConstraint(
36933 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36934 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36937 this.dragSpecs.startSize = size;
36938 this.dragSpecs.startPoint = [x, y];
36939 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36943 * @private Called after the drag operation by the DDProxy
36945 onEndProxyDrag : function(e){
36946 Roo.get(this.proxy).setDisplayed(false);
36947 var endPoint = Roo.lib.Event.getXY(e);
36949 this.overlay.hide();
36952 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36953 newSize = this.dragSpecs.startSize +
36954 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36955 endPoint[0] - this.dragSpecs.startPoint[0] :
36956 this.dragSpecs.startPoint[0] - endPoint[0]
36959 newSize = this.dragSpecs.startSize +
36960 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36961 endPoint[1] - this.dragSpecs.startPoint[1] :
36962 this.dragSpecs.startPoint[1] - endPoint[1]
36965 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36966 if(newSize != this.dragSpecs.startSize){
36967 if(this.fireEvent('beforeapply', this, newSize) !== false){
36968 this.adapter.setElementSize(this, newSize);
36969 this.fireEvent("moved", this, newSize);
36970 this.fireEvent("resize", this, newSize);
36976 * Get the adapter this SplitBar uses
36977 * @return The adapter object
36979 getAdapter : function(){
36980 return this.adapter;
36984 * Set the adapter this SplitBar uses
36985 * @param {Object} adapter A SplitBar adapter object
36987 setAdapter : function(adapter){
36988 this.adapter = adapter;
36989 this.adapter.init(this);
36993 * Gets the minimum size for the resizing element
36994 * @return {Number} The minimum size
36996 getMinimumSize : function(){
36997 return this.minSize;
37001 * Sets the minimum size for the resizing element
37002 * @param {Number} minSize The minimum size
37004 setMinimumSize : function(minSize){
37005 this.minSize = minSize;
37009 * Gets the maximum size for the resizing element
37010 * @return {Number} The maximum size
37012 getMaximumSize : function(){
37013 return this.maxSize;
37017 * Sets the maximum size for the resizing element
37018 * @param {Number} maxSize The maximum size
37020 setMaximumSize : function(maxSize){
37021 this.maxSize = maxSize;
37025 * Sets the initialize size for the resizing element
37026 * @param {Number} size The initial size
37028 setCurrentSize : function(size){
37029 var oldAnimate = this.animate;
37030 this.animate = false;
37031 this.adapter.setElementSize(this, size);
37032 this.animate = oldAnimate;
37036 * Destroy this splitbar.
37037 * @param {Boolean} removeEl True to remove the element
37039 destroy : function(removeEl){
37041 this.shim.remove();
37044 this.proxy.parentNode.removeChild(this.proxy);
37052 * @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.
37054 Roo.bootstrap.SplitBar.createProxy = function(dir){
37055 var proxy = new Roo.Element(document.createElement("div"));
37056 proxy.unselectable();
37057 var cls = 'roo-splitbar-proxy';
37058 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37059 document.body.appendChild(proxy.dom);
37064 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37065 * Default Adapter. It assumes the splitter and resizing element are not positioned
37066 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37068 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37071 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37072 // do nothing for now
37073 init : function(s){
37077 * Called before drag operations to get the current size of the resizing element.
37078 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37080 getElementSize : function(s){
37081 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37082 return s.resizingEl.getWidth();
37084 return s.resizingEl.getHeight();
37089 * Called after drag operations to set the size of the resizing element.
37090 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37091 * @param {Number} newSize The new size to set
37092 * @param {Function} onComplete A function to be invoked when resizing is complete
37094 setElementSize : function(s, newSize, onComplete){
37095 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37097 s.resizingEl.setWidth(newSize);
37099 onComplete(s, newSize);
37102 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37107 s.resizingEl.setHeight(newSize);
37109 onComplete(s, newSize);
37112 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37119 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37120 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37121 * Adapter that moves the splitter element to align with the resized sizing element.
37122 * Used with an absolute positioned SplitBar.
37123 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37124 * document.body, make sure you assign an id to the body element.
37126 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37127 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37128 this.container = Roo.get(container);
37131 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37132 init : function(s){
37133 this.basic.init(s);
37136 getElementSize : function(s){
37137 return this.basic.getElementSize(s);
37140 setElementSize : function(s, newSize, onComplete){
37141 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37144 moveSplitter : function(s){
37145 var yes = Roo.bootstrap.SplitBar;
37146 switch(s.placement){
37148 s.el.setX(s.resizingEl.getRight());
37151 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37154 s.el.setY(s.resizingEl.getBottom());
37157 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37164 * Orientation constant - Create a vertical SplitBar
37168 Roo.bootstrap.SplitBar.VERTICAL = 1;
37171 * Orientation constant - Create a horizontal SplitBar
37175 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37178 * Placement constant - The resizing element is to the left of the splitter element
37182 Roo.bootstrap.SplitBar.LEFT = 1;
37185 * Placement constant - The resizing element is to the right of the splitter element
37189 Roo.bootstrap.SplitBar.RIGHT = 2;
37192 * Placement constant - The resizing element is positioned above the splitter element
37196 Roo.bootstrap.SplitBar.TOP = 3;
37199 * Placement constant - The resizing element is positioned under splitter element
37203 Roo.bootstrap.SplitBar.BOTTOM = 4;
37204 Roo.namespace("Roo.bootstrap.layout");/*
37206 * Ext JS Library 1.1.1
37207 * Copyright(c) 2006-2007, Ext JS, LLC.
37209 * Originally Released Under LGPL - original licence link has changed is not relivant.
37212 * <script type="text/javascript">
37216 * @class Roo.bootstrap.layout.Manager
37217 * @extends Roo.bootstrap.Component
37218 * Base class for layout managers.
37220 Roo.bootstrap.layout.Manager = function(config)
37222 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37228 /** false to disable window resize monitoring @type Boolean */
37229 this.monitorWindowResize = true;
37234 * Fires when a layout is performed.
37235 * @param {Roo.LayoutManager} this
37239 * @event regionresized
37240 * Fires when the user resizes a region.
37241 * @param {Roo.LayoutRegion} region The resized region
37242 * @param {Number} newSize The new size (width for east/west, height for north/south)
37244 "regionresized" : true,
37246 * @event regioncollapsed
37247 * Fires when a region is collapsed.
37248 * @param {Roo.LayoutRegion} region The collapsed region
37250 "regioncollapsed" : true,
37252 * @event regionexpanded
37253 * Fires when a region is expanded.
37254 * @param {Roo.LayoutRegion} region The expanded region
37256 "regionexpanded" : true
37258 this.updating = false;
37261 this.el = Roo.get(config.el);
37267 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37272 monitorWindowResize : true,
37278 onRender : function(ct, position)
37281 this.el = Roo.get(ct);
37284 //this.fireEvent('render',this);
37288 initEvents: function()
37292 // ie scrollbar fix
37293 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37294 document.body.scroll = "no";
37295 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37296 this.el.position('relative');
37298 this.id = this.el.id;
37299 this.el.addClass("roo-layout-container");
37300 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37301 if(this.el.dom != document.body ) {
37302 this.el.on('resize', this.layout,this);
37303 this.el.on('show', this.layout,this);
37309 * Returns true if this layout is currently being updated
37310 * @return {Boolean}
37312 isUpdating : function(){
37313 return this.updating;
37317 * Suspend the LayoutManager from doing auto-layouts while
37318 * making multiple add or remove calls
37320 beginUpdate : function(){
37321 this.updating = true;
37325 * Restore auto-layouts and optionally disable the manager from performing a layout
37326 * @param {Boolean} noLayout true to disable a layout update
37328 endUpdate : function(noLayout){
37329 this.updating = false;
37335 layout: function(){
37339 onRegionResized : function(region, newSize){
37340 this.fireEvent("regionresized", region, newSize);
37344 onRegionCollapsed : function(region){
37345 this.fireEvent("regioncollapsed", region);
37348 onRegionExpanded : function(region){
37349 this.fireEvent("regionexpanded", region);
37353 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37354 * performs box-model adjustments.
37355 * @return {Object} The size as an object {width: (the width), height: (the height)}
37357 getViewSize : function()
37360 if(this.el.dom != document.body){
37361 size = this.el.getSize();
37363 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37365 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37366 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37371 * Returns the Element this layout is bound to.
37372 * @return {Roo.Element}
37374 getEl : function(){
37379 * Returns the specified region.
37380 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37381 * @return {Roo.LayoutRegion}
37383 getRegion : function(target){
37384 return this.regions[target.toLowerCase()];
37387 onWindowResize : function(){
37388 if(this.monitorWindowResize){
37395 * Ext JS Library 1.1.1
37396 * Copyright(c) 2006-2007, Ext JS, LLC.
37398 * Originally Released Under LGPL - original licence link has changed is not relivant.
37401 * <script type="text/javascript">
37404 * @class Roo.bootstrap.layout.Border
37405 * @extends Roo.bootstrap.layout.Manager
37406 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37407 * please see: examples/bootstrap/nested.html<br><br>
37409 <b>The container the layout is rendered into can be either the body element or any other element.
37410 If it is not the body element, the container needs to either be an absolute positioned element,
37411 or you will need to add "position:relative" to the css of the container. You will also need to specify
37412 the container size if it is not the body element.</b>
37415 * Create a new Border
37416 * @param {Object} config Configuration options
37418 Roo.bootstrap.layout.Border = function(config){
37419 config = config || {};
37420 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37424 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37425 if(config[region]){
37426 config[region].region = region;
37427 this.addRegion(config[region]);
37433 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37435 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37437 parent : false, // this might point to a 'nest' or a ???
37440 * Creates and adds a new region if it doesn't already exist.
37441 * @param {String} target The target region key (north, south, east, west or center).
37442 * @param {Object} config The regions config object
37443 * @return {BorderLayoutRegion} The new region
37445 addRegion : function(config)
37447 if(!this.regions[config.region]){
37448 var r = this.factory(config);
37449 this.bindRegion(r);
37451 return this.regions[config.region];
37455 bindRegion : function(r){
37456 this.regions[r.config.region] = r;
37458 r.on("visibilitychange", this.layout, this);
37459 r.on("paneladded", this.layout, this);
37460 r.on("panelremoved", this.layout, this);
37461 r.on("invalidated", this.layout, this);
37462 r.on("resized", this.onRegionResized, this);
37463 r.on("collapsed", this.onRegionCollapsed, this);
37464 r.on("expanded", this.onRegionExpanded, this);
37468 * Performs a layout update.
37470 layout : function()
37472 if(this.updating) {
37476 // render all the rebions if they have not been done alreayd?
37477 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37478 if(this.regions[region] && !this.regions[region].bodyEl){
37479 this.regions[region].onRender(this.el)
37483 var size = this.getViewSize();
37484 var w = size.width;
37485 var h = size.height;
37490 //var x = 0, y = 0;
37492 var rs = this.regions;
37493 var north = rs["north"];
37494 var south = rs["south"];
37495 var west = rs["west"];
37496 var east = rs["east"];
37497 var center = rs["center"];
37498 //if(this.hideOnLayout){ // not supported anymore
37499 //c.el.setStyle("display", "none");
37501 if(north && north.isVisible()){
37502 var b = north.getBox();
37503 var m = north.getMargins();
37504 b.width = w - (m.left+m.right);
37507 centerY = b.height + b.y + m.bottom;
37508 centerH -= centerY;
37509 north.updateBox(this.safeBox(b));
37511 if(south && south.isVisible()){
37512 var b = south.getBox();
37513 var m = south.getMargins();
37514 b.width = w - (m.left+m.right);
37516 var totalHeight = (b.height + m.top + m.bottom);
37517 b.y = h - totalHeight + m.top;
37518 centerH -= totalHeight;
37519 south.updateBox(this.safeBox(b));
37521 if(west && west.isVisible()){
37522 var b = west.getBox();
37523 var m = west.getMargins();
37524 b.height = centerH - (m.top+m.bottom);
37526 b.y = centerY + m.top;
37527 var totalWidth = (b.width + m.left + m.right);
37528 centerX += totalWidth;
37529 centerW -= totalWidth;
37530 west.updateBox(this.safeBox(b));
37532 if(east && east.isVisible()){
37533 var b = east.getBox();
37534 var m = east.getMargins();
37535 b.height = centerH - (m.top+m.bottom);
37536 var totalWidth = (b.width + m.left + m.right);
37537 b.x = w - totalWidth + m.left;
37538 b.y = centerY + m.top;
37539 centerW -= totalWidth;
37540 east.updateBox(this.safeBox(b));
37543 var m = center.getMargins();
37545 x: centerX + m.left,
37546 y: centerY + m.top,
37547 width: centerW - (m.left+m.right),
37548 height: centerH - (m.top+m.bottom)
37550 //if(this.hideOnLayout){
37551 //center.el.setStyle("display", "block");
37553 center.updateBox(this.safeBox(centerBox));
37556 this.fireEvent("layout", this);
37560 safeBox : function(box){
37561 box.width = Math.max(0, box.width);
37562 box.height = Math.max(0, box.height);
37567 * Adds a ContentPanel (or subclass) to this layout.
37568 * @param {String} target The target region key (north, south, east, west or center).
37569 * @param {Roo.ContentPanel} panel The panel to add
37570 * @return {Roo.ContentPanel} The added panel
37572 add : function(target, panel){
37574 target = target.toLowerCase();
37575 return this.regions[target].add(panel);
37579 * Remove a ContentPanel (or subclass) to this layout.
37580 * @param {String} target The target region key (north, south, east, west or center).
37581 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37582 * @return {Roo.ContentPanel} The removed panel
37584 remove : function(target, panel){
37585 target = target.toLowerCase();
37586 return this.regions[target].remove(panel);
37590 * Searches all regions for a panel with the specified id
37591 * @param {String} panelId
37592 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37594 findPanel : function(panelId){
37595 var rs = this.regions;
37596 for(var target in rs){
37597 if(typeof rs[target] != "function"){
37598 var p = rs[target].getPanel(panelId);
37608 * Searches all regions for a panel with the specified id and activates (shows) it.
37609 * @param {String/ContentPanel} panelId The panels id or the panel itself
37610 * @return {Roo.ContentPanel} The shown panel or null
37612 showPanel : function(panelId) {
37613 var rs = this.regions;
37614 for(var target in rs){
37615 var r = rs[target];
37616 if(typeof r != "function"){
37617 if(r.hasPanel(panelId)){
37618 return r.showPanel(panelId);
37626 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37627 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37630 restoreState : function(provider){
37632 provider = Roo.state.Manager;
37634 var sm = new Roo.LayoutStateManager();
37635 sm.init(this, provider);
37641 * Adds a xtype elements to the layout.
37645 xtype : 'ContentPanel',
37652 xtype : 'NestedLayoutPanel',
37658 items : [ ... list of content panels or nested layout panels.. ]
37662 * @param {Object} cfg Xtype definition of item to add.
37664 addxtype : function(cfg)
37666 // basically accepts a pannel...
37667 // can accept a layout region..!?!?
37668 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37671 // theory? children can only be panels??
37673 //if (!cfg.xtype.match(/Panel$/)) {
37678 if (typeof(cfg.region) == 'undefined') {
37679 Roo.log("Failed to add Panel, region was not set");
37683 var region = cfg.region;
37689 xitems = cfg.items;
37694 if ( region == 'center') {
37695 Roo.log("Center: " + cfg.title);
37701 case 'Content': // ContentPanel (el, cfg)
37702 case 'Scroll': // ContentPanel (el, cfg)
37704 cfg.autoCreate = cfg.autoCreate || true;
37705 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37707 // var el = this.el.createChild();
37708 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37711 this.add(region, ret);
37715 case 'TreePanel': // our new panel!
37716 cfg.el = this.el.createChild();
37717 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37718 this.add(region, ret);
37723 // create a new Layout (which is a Border Layout...
37725 var clayout = cfg.layout;
37726 clayout.el = this.el.createChild();
37727 clayout.items = clayout.items || [];
37731 // replace this exitems with the clayout ones..
37732 xitems = clayout.items;
37734 // force background off if it's in center...
37735 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37736 cfg.background = false;
37738 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37741 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37742 //console.log('adding nested layout panel ' + cfg.toSource());
37743 this.add(region, ret);
37744 nb = {}; /// find first...
37749 // needs grid and region
37751 //var el = this.getRegion(region).el.createChild();
37753 *var el = this.el.createChild();
37754 // create the grid first...
37755 cfg.grid.container = el;
37756 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37759 if (region == 'center' && this.active ) {
37760 cfg.background = false;
37763 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37765 this.add(region, ret);
37767 if (cfg.background) {
37768 // render grid on panel activation (if panel background)
37769 ret.on('activate', function(gp) {
37770 if (!gp.grid.rendered) {
37771 // gp.grid.render(el);
37775 // cfg.grid.render(el);
37781 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37782 // it was the old xcomponent building that caused this before.
37783 // espeically if border is the top element in the tree.
37793 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37795 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37796 this.add(region, ret);
37800 throw "Can not add '" + cfg.xtype + "' to Border";
37806 this.beginUpdate();
37810 Roo.each(xitems, function(i) {
37811 region = nb && i.region ? i.region : false;
37813 var add = ret.addxtype(i);
37816 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37817 if (!i.background) {
37818 abn[region] = nb[region] ;
37825 // make the last non-background panel active..
37826 //if (nb) { Roo.log(abn); }
37829 for(var r in abn) {
37830 region = this.getRegion(r);
37832 // tried using nb[r], but it does not work..
37834 region.showPanel(abn[r]);
37845 factory : function(cfg)
37848 var validRegions = Roo.bootstrap.layout.Border.regions;
37850 var target = cfg.region;
37853 var r = Roo.bootstrap.layout;
37857 return new r.North(cfg);
37859 return new r.South(cfg);
37861 return new r.East(cfg);
37863 return new r.West(cfg);
37865 return new r.Center(cfg);
37867 throw 'Layout region "'+target+'" not supported.';
37874 * Ext JS Library 1.1.1
37875 * Copyright(c) 2006-2007, Ext JS, LLC.
37877 * Originally Released Under LGPL - original licence link has changed is not relivant.
37880 * <script type="text/javascript">
37884 * @class Roo.bootstrap.layout.Basic
37885 * @extends Roo.util.Observable
37886 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37887 * and does not have a titlebar, tabs or any other features. All it does is size and position
37888 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37889 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37890 * @cfg {string} region the region that it inhabits..
37891 * @cfg {bool} skipConfig skip config?
37895 Roo.bootstrap.layout.Basic = function(config){
37897 this.mgr = config.mgr;
37899 this.position = config.region;
37901 var skipConfig = config.skipConfig;
37905 * @scope Roo.BasicLayoutRegion
37909 * @event beforeremove
37910 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37911 * @param {Roo.LayoutRegion} this
37912 * @param {Roo.ContentPanel} panel The panel
37913 * @param {Object} e The cancel event object
37915 "beforeremove" : true,
37917 * @event invalidated
37918 * Fires when the layout for this region is changed.
37919 * @param {Roo.LayoutRegion} this
37921 "invalidated" : true,
37923 * @event visibilitychange
37924 * Fires when this region is shown or hidden
37925 * @param {Roo.LayoutRegion} this
37926 * @param {Boolean} visibility true or false
37928 "visibilitychange" : true,
37930 * @event paneladded
37931 * Fires when a panel is added.
37932 * @param {Roo.LayoutRegion} this
37933 * @param {Roo.ContentPanel} panel The panel
37935 "paneladded" : true,
37937 * @event panelremoved
37938 * Fires when a panel is removed.
37939 * @param {Roo.LayoutRegion} this
37940 * @param {Roo.ContentPanel} panel The panel
37942 "panelremoved" : true,
37944 * @event beforecollapse
37945 * Fires when this region before collapse.
37946 * @param {Roo.LayoutRegion} this
37948 "beforecollapse" : true,
37951 * Fires when this region is collapsed.
37952 * @param {Roo.LayoutRegion} this
37954 "collapsed" : true,
37957 * Fires when this region is expanded.
37958 * @param {Roo.LayoutRegion} this
37963 * Fires when this region is slid into view.
37964 * @param {Roo.LayoutRegion} this
37966 "slideshow" : true,
37969 * Fires when this region slides out of view.
37970 * @param {Roo.LayoutRegion} this
37972 "slidehide" : true,
37974 * @event panelactivated
37975 * Fires when a panel is activated.
37976 * @param {Roo.LayoutRegion} this
37977 * @param {Roo.ContentPanel} panel The activated panel
37979 "panelactivated" : true,
37982 * Fires when the user resizes this region.
37983 * @param {Roo.LayoutRegion} this
37984 * @param {Number} newSize The new size (width for east/west, height for north/south)
37988 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37989 this.panels = new Roo.util.MixedCollection();
37990 this.panels.getKey = this.getPanelId.createDelegate(this);
37992 this.activePanel = null;
37993 // ensure listeners are added...
37995 if (config.listeners || config.events) {
37996 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37997 listeners : config.listeners || {},
37998 events : config.events || {}
38002 if(skipConfig !== true){
38003 this.applyConfig(config);
38007 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38009 getPanelId : function(p){
38013 applyConfig : function(config){
38014 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38015 this.config = config;
38020 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38021 * the width, for horizontal (north, south) the height.
38022 * @param {Number} newSize The new width or height
38024 resizeTo : function(newSize){
38025 var el = this.el ? this.el :
38026 (this.activePanel ? this.activePanel.getEl() : null);
38028 switch(this.position){
38031 el.setWidth(newSize);
38032 this.fireEvent("resized", this, newSize);
38036 el.setHeight(newSize);
38037 this.fireEvent("resized", this, newSize);
38043 getBox : function(){
38044 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38047 getMargins : function(){
38048 return this.margins;
38051 updateBox : function(box){
38053 var el = this.activePanel.getEl();
38054 el.dom.style.left = box.x + "px";
38055 el.dom.style.top = box.y + "px";
38056 this.activePanel.setSize(box.width, box.height);
38060 * Returns the container element for this region.
38061 * @return {Roo.Element}
38063 getEl : function(){
38064 return this.activePanel;
38068 * Returns true if this region is currently visible.
38069 * @return {Boolean}
38071 isVisible : function(){
38072 return this.activePanel ? true : false;
38075 setActivePanel : function(panel){
38076 panel = this.getPanel(panel);
38077 if(this.activePanel && this.activePanel != panel){
38078 this.activePanel.setActiveState(false);
38079 this.activePanel.getEl().setLeftTop(-10000,-10000);
38081 this.activePanel = panel;
38082 panel.setActiveState(true);
38084 panel.setSize(this.box.width, this.box.height);
38086 this.fireEvent("panelactivated", this, panel);
38087 this.fireEvent("invalidated");
38091 * Show the specified panel.
38092 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38093 * @return {Roo.ContentPanel} The shown panel or null
38095 showPanel : function(panel){
38096 panel = this.getPanel(panel);
38098 this.setActivePanel(panel);
38104 * Get the active panel for this region.
38105 * @return {Roo.ContentPanel} The active panel or null
38107 getActivePanel : function(){
38108 return this.activePanel;
38112 * Add the passed ContentPanel(s)
38113 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38114 * @return {Roo.ContentPanel} The panel added (if only one was added)
38116 add : function(panel){
38117 if(arguments.length > 1){
38118 for(var i = 0, len = arguments.length; i < len; i++) {
38119 this.add(arguments[i]);
38123 if(this.hasPanel(panel)){
38124 this.showPanel(panel);
38127 var el = panel.getEl();
38128 if(el.dom.parentNode != this.mgr.el.dom){
38129 this.mgr.el.dom.appendChild(el.dom);
38131 if(panel.setRegion){
38132 panel.setRegion(this);
38134 this.panels.add(panel);
38135 el.setStyle("position", "absolute");
38136 if(!panel.background){
38137 this.setActivePanel(panel);
38138 if(this.config.initialSize && this.panels.getCount()==1){
38139 this.resizeTo(this.config.initialSize);
38142 this.fireEvent("paneladded", this, panel);
38147 * Returns true if the panel is in this region.
38148 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38149 * @return {Boolean}
38151 hasPanel : function(panel){
38152 if(typeof panel == "object"){ // must be panel obj
38153 panel = panel.getId();
38155 return this.getPanel(panel) ? true : false;
38159 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38160 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38161 * @param {Boolean} preservePanel Overrides the config preservePanel option
38162 * @return {Roo.ContentPanel} The panel that was removed
38164 remove : function(panel, preservePanel){
38165 panel = this.getPanel(panel);
38170 this.fireEvent("beforeremove", this, panel, e);
38171 if(e.cancel === true){
38174 var panelId = panel.getId();
38175 this.panels.removeKey(panelId);
38180 * Returns the panel specified or null if it's not in this region.
38181 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38182 * @return {Roo.ContentPanel}
38184 getPanel : function(id){
38185 if(typeof id == "object"){ // must be panel obj
38188 return this.panels.get(id);
38192 * Returns this regions position (north/south/east/west/center).
38195 getPosition: function(){
38196 return this.position;
38200 * Ext JS Library 1.1.1
38201 * Copyright(c) 2006-2007, Ext JS, LLC.
38203 * Originally Released Under LGPL - original licence link has changed is not relivant.
38206 * <script type="text/javascript">
38210 * @class Roo.bootstrap.layout.Region
38211 * @extends Roo.bootstrap.layout.Basic
38212 * This class represents a region in a layout manager.
38214 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38215 * @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})
38216 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38217 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38218 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38219 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38220 * @cfg {String} title The title for the region (overrides panel titles)
38221 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38222 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38223 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38224 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38225 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38226 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38227 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38228 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38229 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38230 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38232 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38233 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38234 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38235 * @cfg {Number} width For East/West panels
38236 * @cfg {Number} height For North/South panels
38237 * @cfg {Boolean} split To show the splitter
38238 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38240 * @cfg {string} cls Extra CSS classes to add to region
38242 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38243 * @cfg {string} region the region that it inhabits..
38246 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38247 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38249 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38250 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38251 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38253 Roo.bootstrap.layout.Region = function(config)
38255 this.applyConfig(config);
38257 var mgr = config.mgr;
38258 var pos = config.region;
38259 config.skipConfig = true;
38260 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38263 this.onRender(mgr.el);
38266 this.visible = true;
38267 this.collapsed = false;
38268 this.unrendered_panels = [];
38271 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38273 position: '', // set by wrapper (eg. north/south etc..)
38274 unrendered_panels : null, // unrendered panels.
38276 tabPosition : false,
38278 mgr: false, // points to 'Border'
38281 createBody : function(){
38282 /** This region's body element
38283 * @type Roo.Element */
38284 this.bodyEl = this.el.createChild({
38286 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38290 onRender: function(ctr, pos)
38292 var dh = Roo.DomHelper;
38293 /** This region's container element
38294 * @type Roo.Element */
38295 this.el = dh.append(ctr.dom, {
38297 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38299 /** This region's title element
38300 * @type Roo.Element */
38302 this.titleEl = dh.append(this.el.dom, {
38304 unselectable: "on",
38305 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38307 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38308 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38312 this.titleEl.enableDisplayMode();
38313 /** This region's title text element
38314 * @type HTMLElement */
38315 this.titleTextEl = this.titleEl.dom.firstChild;
38316 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38318 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38319 this.closeBtn.enableDisplayMode();
38320 this.closeBtn.on("click", this.closeClicked, this);
38321 this.closeBtn.hide();
38323 this.createBody(this.config);
38324 if(this.config.hideWhenEmpty){
38326 this.on("paneladded", this.validateVisibility, this);
38327 this.on("panelremoved", this.validateVisibility, this);
38329 if(this.autoScroll){
38330 this.bodyEl.setStyle("overflow", "auto");
38332 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38334 //if(c.titlebar !== false){
38335 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38336 this.titleEl.hide();
38338 this.titleEl.show();
38339 if(this.config.title){
38340 this.titleTextEl.innerHTML = this.config.title;
38344 if(this.config.collapsed){
38345 this.collapse(true);
38347 if(this.config.hidden){
38351 if (this.unrendered_panels && this.unrendered_panels.length) {
38352 for (var i =0;i< this.unrendered_panels.length; i++) {
38353 this.add(this.unrendered_panels[i]);
38355 this.unrendered_panels = null;
38361 applyConfig : function(c)
38364 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38365 var dh = Roo.DomHelper;
38366 if(c.titlebar !== false){
38367 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38368 this.collapseBtn.on("click", this.collapse, this);
38369 this.collapseBtn.enableDisplayMode();
38371 if(c.showPin === true || this.showPin){
38372 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38373 this.stickBtn.enableDisplayMode();
38374 this.stickBtn.on("click", this.expand, this);
38375 this.stickBtn.hide();
38380 /** This region's collapsed element
38381 * @type Roo.Element */
38384 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38385 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38388 if(c.floatable !== false){
38389 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38390 this.collapsedEl.on("click", this.collapseClick, this);
38393 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38394 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38395 id: "message", unselectable: "on", style:{"float":"left"}});
38396 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38398 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38399 this.expandBtn.on("click", this.expand, this);
38403 if(this.collapseBtn){
38404 this.collapseBtn.setVisible(c.collapsible == true);
38407 this.cmargins = c.cmargins || this.cmargins ||
38408 (this.position == "west" || this.position == "east" ?
38409 {top: 0, left: 2, right:2, bottom: 0} :
38410 {top: 2, left: 0, right:0, bottom: 2});
38412 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38415 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38417 this.autoScroll = c.autoScroll || false;
38422 this.duration = c.duration || .30;
38423 this.slideDuration = c.slideDuration || .45;
38428 * Returns true if this region is currently visible.
38429 * @return {Boolean}
38431 isVisible : function(){
38432 return this.visible;
38436 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38437 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38439 //setCollapsedTitle : function(title){
38440 // title = title || " ";
38441 // if(this.collapsedTitleTextEl){
38442 // this.collapsedTitleTextEl.innerHTML = title;
38446 getBox : function(){
38448 // if(!this.collapsed){
38449 b = this.el.getBox(false, true);
38451 // b = this.collapsedEl.getBox(false, true);
38456 getMargins : function(){
38457 return this.margins;
38458 //return this.collapsed ? this.cmargins : this.margins;
38461 highlight : function(){
38462 this.el.addClass("x-layout-panel-dragover");
38465 unhighlight : function(){
38466 this.el.removeClass("x-layout-panel-dragover");
38469 updateBox : function(box)
38471 if (!this.bodyEl) {
38472 return; // not rendered yet..
38476 if(!this.collapsed){
38477 this.el.dom.style.left = box.x + "px";
38478 this.el.dom.style.top = box.y + "px";
38479 this.updateBody(box.width, box.height);
38481 this.collapsedEl.dom.style.left = box.x + "px";
38482 this.collapsedEl.dom.style.top = box.y + "px";
38483 this.collapsedEl.setSize(box.width, box.height);
38486 this.tabs.autoSizeTabs();
38490 updateBody : function(w, h)
38493 this.el.setWidth(w);
38494 w -= this.el.getBorderWidth("rl");
38495 if(this.config.adjustments){
38496 w += this.config.adjustments[0];
38499 if(h !== null && h > 0){
38500 this.el.setHeight(h);
38501 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38502 h -= this.el.getBorderWidth("tb");
38503 if(this.config.adjustments){
38504 h += this.config.adjustments[1];
38506 this.bodyEl.setHeight(h);
38508 h = this.tabs.syncHeight(h);
38511 if(this.panelSize){
38512 w = w !== null ? w : this.panelSize.width;
38513 h = h !== null ? h : this.panelSize.height;
38515 if(this.activePanel){
38516 var el = this.activePanel.getEl();
38517 w = w !== null ? w : el.getWidth();
38518 h = h !== null ? h : el.getHeight();
38519 this.panelSize = {width: w, height: h};
38520 this.activePanel.setSize(w, h);
38522 if(Roo.isIE && this.tabs){
38523 this.tabs.el.repaint();
38528 * Returns the container element for this region.
38529 * @return {Roo.Element}
38531 getEl : function(){
38536 * Hides this region.
38539 //if(!this.collapsed){
38540 this.el.dom.style.left = "-2000px";
38543 // this.collapsedEl.dom.style.left = "-2000px";
38544 // this.collapsedEl.hide();
38546 this.visible = false;
38547 this.fireEvent("visibilitychange", this, false);
38551 * Shows this region if it was previously hidden.
38554 //if(!this.collapsed){
38557 // this.collapsedEl.show();
38559 this.visible = true;
38560 this.fireEvent("visibilitychange", this, true);
38563 closeClicked : function(){
38564 if(this.activePanel){
38565 this.remove(this.activePanel);
38569 collapseClick : function(e){
38571 e.stopPropagation();
38574 e.stopPropagation();
38580 * Collapses this region.
38581 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38584 collapse : function(skipAnim, skipCheck = false){
38585 if(this.collapsed) {
38589 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38591 this.collapsed = true;
38593 this.split.el.hide();
38595 if(this.config.animate && skipAnim !== true){
38596 this.fireEvent("invalidated", this);
38597 this.animateCollapse();
38599 this.el.setLocation(-20000,-20000);
38601 this.collapsedEl.show();
38602 this.fireEvent("collapsed", this);
38603 this.fireEvent("invalidated", this);
38609 animateCollapse : function(){
38614 * Expands this region if it was previously collapsed.
38615 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38616 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38619 expand : function(e, skipAnim){
38621 e.stopPropagation();
38623 if(!this.collapsed || this.el.hasActiveFx()) {
38627 this.afterSlideIn();
38630 this.collapsed = false;
38631 if(this.config.animate && skipAnim !== true){
38632 this.animateExpand();
38636 this.split.el.show();
38638 this.collapsedEl.setLocation(-2000,-2000);
38639 this.collapsedEl.hide();
38640 this.fireEvent("invalidated", this);
38641 this.fireEvent("expanded", this);
38645 animateExpand : function(){
38649 initTabs : function()
38651 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38653 var ts = new Roo.bootstrap.panel.Tabs({
38654 el: this.bodyEl.dom,
38656 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38657 disableTooltips: this.config.disableTabTips,
38658 toolbar : this.config.toolbar
38661 if(this.config.hideTabs){
38662 ts.stripWrap.setDisplayed(false);
38665 ts.resizeTabs = this.config.resizeTabs === true;
38666 ts.minTabWidth = this.config.minTabWidth || 40;
38667 ts.maxTabWidth = this.config.maxTabWidth || 250;
38668 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38669 ts.monitorResize = false;
38670 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38671 ts.bodyEl.addClass('roo-layout-tabs-body');
38672 this.panels.each(this.initPanelAsTab, this);
38675 initPanelAsTab : function(panel){
38676 var ti = this.tabs.addTab(
38680 this.config.closeOnTab && panel.isClosable(),
38683 if(panel.tabTip !== undefined){
38684 ti.setTooltip(panel.tabTip);
38686 ti.on("activate", function(){
38687 this.setActivePanel(panel);
38690 if(this.config.closeOnTab){
38691 ti.on("beforeclose", function(t, e){
38693 this.remove(panel);
38697 panel.tabItem = ti;
38702 updatePanelTitle : function(panel, title)
38704 if(this.activePanel == panel){
38705 this.updateTitle(title);
38708 var ti = this.tabs.getTab(panel.getEl().id);
38710 if(panel.tabTip !== undefined){
38711 ti.setTooltip(panel.tabTip);
38716 updateTitle : function(title){
38717 if(this.titleTextEl && !this.config.title){
38718 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38722 setActivePanel : function(panel)
38724 panel = this.getPanel(panel);
38725 if(this.activePanel && this.activePanel != panel){
38726 if(this.activePanel.setActiveState(false) === false){
38730 this.activePanel = panel;
38731 panel.setActiveState(true);
38732 if(this.panelSize){
38733 panel.setSize(this.panelSize.width, this.panelSize.height);
38736 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38738 this.updateTitle(panel.getTitle());
38740 this.fireEvent("invalidated", this);
38742 this.fireEvent("panelactivated", this, panel);
38746 * Shows the specified panel.
38747 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38748 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38750 showPanel : function(panel)
38752 panel = this.getPanel(panel);
38755 var tab = this.tabs.getTab(panel.getEl().id);
38756 if(tab.isHidden()){
38757 this.tabs.unhideTab(tab.id);
38761 this.setActivePanel(panel);
38768 * Get the active panel for this region.
38769 * @return {Roo.ContentPanel} The active panel or null
38771 getActivePanel : function(){
38772 return this.activePanel;
38775 validateVisibility : function(){
38776 if(this.panels.getCount() < 1){
38777 this.updateTitle(" ");
38778 this.closeBtn.hide();
38781 if(!this.isVisible()){
38788 * Adds the passed ContentPanel(s) to this region.
38789 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38790 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38792 add : function(panel)
38794 if(arguments.length > 1){
38795 for(var i = 0, len = arguments.length; i < len; i++) {
38796 this.add(arguments[i]);
38801 // if we have not been rendered yet, then we can not really do much of this..
38802 if (!this.bodyEl) {
38803 this.unrendered_panels.push(panel);
38810 if(this.hasPanel(panel)){
38811 this.showPanel(panel);
38814 panel.setRegion(this);
38815 this.panels.add(panel);
38816 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38817 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38818 // and hide them... ???
38819 this.bodyEl.dom.appendChild(panel.getEl().dom);
38820 if(panel.background !== true){
38821 this.setActivePanel(panel);
38823 this.fireEvent("paneladded", this, panel);
38830 this.initPanelAsTab(panel);
38834 if(panel.background !== true){
38835 this.tabs.activate(panel.getEl().id);
38837 this.fireEvent("paneladded", this, panel);
38842 * Hides the tab for the specified panel.
38843 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38845 hidePanel : function(panel){
38846 if(this.tabs && (panel = this.getPanel(panel))){
38847 this.tabs.hideTab(panel.getEl().id);
38852 * Unhides the tab for a previously hidden panel.
38853 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38855 unhidePanel : function(panel){
38856 if(this.tabs && (panel = this.getPanel(panel))){
38857 this.tabs.unhideTab(panel.getEl().id);
38861 clearPanels : function(){
38862 while(this.panels.getCount() > 0){
38863 this.remove(this.panels.first());
38868 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38869 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38870 * @param {Boolean} preservePanel Overrides the config preservePanel option
38871 * @return {Roo.ContentPanel} The panel that was removed
38873 remove : function(panel, preservePanel)
38875 panel = this.getPanel(panel);
38880 this.fireEvent("beforeremove", this, panel, e);
38881 if(e.cancel === true){
38884 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38885 var panelId = panel.getId();
38886 this.panels.removeKey(panelId);
38888 document.body.appendChild(panel.getEl().dom);
38891 this.tabs.removeTab(panel.getEl().id);
38892 }else if (!preservePanel){
38893 this.bodyEl.dom.removeChild(panel.getEl().dom);
38895 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38896 var p = this.panels.first();
38897 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38898 tempEl.appendChild(p.getEl().dom);
38899 this.bodyEl.update("");
38900 this.bodyEl.dom.appendChild(p.getEl().dom);
38902 this.updateTitle(p.getTitle());
38904 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38905 this.setActivePanel(p);
38907 panel.setRegion(null);
38908 if(this.activePanel == panel){
38909 this.activePanel = null;
38911 if(this.config.autoDestroy !== false && preservePanel !== true){
38912 try{panel.destroy();}catch(e){}
38914 this.fireEvent("panelremoved", this, panel);
38919 * Returns the TabPanel component used by this region
38920 * @return {Roo.TabPanel}
38922 getTabs : function(){
38926 createTool : function(parentEl, className){
38927 var btn = Roo.DomHelper.append(parentEl, {
38929 cls: "x-layout-tools-button",
38932 cls: "roo-layout-tools-button-inner " + className,
38936 btn.addClassOnOver("roo-layout-tools-button-over");
38941 * Ext JS Library 1.1.1
38942 * Copyright(c) 2006-2007, Ext JS, LLC.
38944 * Originally Released Under LGPL - original licence link has changed is not relivant.
38947 * <script type="text/javascript">
38953 * @class Roo.SplitLayoutRegion
38954 * @extends Roo.LayoutRegion
38955 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38957 Roo.bootstrap.layout.Split = function(config){
38958 this.cursor = config.cursor;
38959 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38962 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38964 splitTip : "Drag to resize.",
38965 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38966 useSplitTips : false,
38968 applyConfig : function(config){
38969 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38972 onRender : function(ctr,pos) {
38974 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38975 if(!this.config.split){
38980 var splitEl = Roo.DomHelper.append(ctr.dom, {
38982 id: this.el.id + "-split",
38983 cls: "roo-layout-split roo-layout-split-"+this.position,
38986 /** The SplitBar for this region
38987 * @type Roo.SplitBar */
38988 // does not exist yet...
38989 Roo.log([this.position, this.orientation]);
38991 this.split = new Roo.bootstrap.SplitBar({
38992 dragElement : splitEl,
38993 resizingElement: this.el,
38994 orientation : this.orientation
38997 this.split.on("moved", this.onSplitMove, this);
38998 this.split.useShim = this.config.useShim === true;
38999 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39000 if(this.useSplitTips){
39001 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39003 //if(config.collapsible){
39004 // this.split.el.on("dblclick", this.collapse, this);
39007 if(typeof this.config.minSize != "undefined"){
39008 this.split.minSize = this.config.minSize;
39010 if(typeof this.config.maxSize != "undefined"){
39011 this.split.maxSize = this.config.maxSize;
39013 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39014 this.hideSplitter();
39019 getHMaxSize : function(){
39020 var cmax = this.config.maxSize || 10000;
39021 var center = this.mgr.getRegion("center");
39022 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39025 getVMaxSize : function(){
39026 var cmax = this.config.maxSize || 10000;
39027 var center = this.mgr.getRegion("center");
39028 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39031 onSplitMove : function(split, newSize){
39032 this.fireEvent("resized", this, newSize);
39036 * Returns the {@link Roo.SplitBar} for this region.
39037 * @return {Roo.SplitBar}
39039 getSplitBar : function(){
39044 this.hideSplitter();
39045 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39048 hideSplitter : function(){
39050 this.split.el.setLocation(-2000,-2000);
39051 this.split.el.hide();
39057 this.split.el.show();
39059 Roo.bootstrap.layout.Split.superclass.show.call(this);
39062 beforeSlide: function(){
39063 if(Roo.isGecko){// firefox overflow auto bug workaround
39064 this.bodyEl.clip();
39066 this.tabs.bodyEl.clip();
39068 if(this.activePanel){
39069 this.activePanel.getEl().clip();
39071 if(this.activePanel.beforeSlide){
39072 this.activePanel.beforeSlide();
39078 afterSlide : function(){
39079 if(Roo.isGecko){// firefox overflow auto bug workaround
39080 this.bodyEl.unclip();
39082 this.tabs.bodyEl.unclip();
39084 if(this.activePanel){
39085 this.activePanel.getEl().unclip();
39086 if(this.activePanel.afterSlide){
39087 this.activePanel.afterSlide();
39093 initAutoHide : function(){
39094 if(this.autoHide !== false){
39095 if(!this.autoHideHd){
39096 var st = new Roo.util.DelayedTask(this.slideIn, this);
39097 this.autoHideHd = {
39098 "mouseout": function(e){
39099 if(!e.within(this.el, true)){
39103 "mouseover" : function(e){
39109 this.el.on(this.autoHideHd);
39113 clearAutoHide : function(){
39114 if(this.autoHide !== false){
39115 this.el.un("mouseout", this.autoHideHd.mouseout);
39116 this.el.un("mouseover", this.autoHideHd.mouseover);
39120 clearMonitor : function(){
39121 Roo.get(document).un("click", this.slideInIf, this);
39124 // these names are backwards but not changed for compat
39125 slideOut : function(){
39126 if(this.isSlid || this.el.hasActiveFx()){
39129 this.isSlid = true;
39130 if(this.collapseBtn){
39131 this.collapseBtn.hide();
39133 this.closeBtnState = this.closeBtn.getStyle('display');
39134 this.closeBtn.hide();
39136 this.stickBtn.show();
39139 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39140 this.beforeSlide();
39141 this.el.setStyle("z-index", 10001);
39142 this.el.slideIn(this.getSlideAnchor(), {
39143 callback: function(){
39145 this.initAutoHide();
39146 Roo.get(document).on("click", this.slideInIf, this);
39147 this.fireEvent("slideshow", this);
39154 afterSlideIn : function(){
39155 this.clearAutoHide();
39156 this.isSlid = false;
39157 this.clearMonitor();
39158 this.el.setStyle("z-index", "");
39159 if(this.collapseBtn){
39160 this.collapseBtn.show();
39162 this.closeBtn.setStyle('display', this.closeBtnState);
39164 this.stickBtn.hide();
39166 this.fireEvent("slidehide", this);
39169 slideIn : function(cb){
39170 if(!this.isSlid || this.el.hasActiveFx()){
39174 this.isSlid = false;
39175 this.beforeSlide();
39176 this.el.slideOut(this.getSlideAnchor(), {
39177 callback: function(){
39178 this.el.setLeftTop(-10000, -10000);
39180 this.afterSlideIn();
39188 slideInIf : function(e){
39189 if(!e.within(this.el)){
39194 animateCollapse : function(){
39195 this.beforeSlide();
39196 this.el.setStyle("z-index", 20000);
39197 var anchor = this.getSlideAnchor();
39198 this.el.slideOut(anchor, {
39199 callback : function(){
39200 this.el.setStyle("z-index", "");
39201 this.collapsedEl.slideIn(anchor, {duration:.3});
39203 this.el.setLocation(-10000,-10000);
39205 this.fireEvent("collapsed", this);
39212 animateExpand : function(){
39213 this.beforeSlide();
39214 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39215 this.el.setStyle("z-index", 20000);
39216 this.collapsedEl.hide({
39219 this.el.slideIn(this.getSlideAnchor(), {
39220 callback : function(){
39221 this.el.setStyle("z-index", "");
39224 this.split.el.show();
39226 this.fireEvent("invalidated", this);
39227 this.fireEvent("expanded", this);
39255 getAnchor : function(){
39256 return this.anchors[this.position];
39259 getCollapseAnchor : function(){
39260 return this.canchors[this.position];
39263 getSlideAnchor : function(){
39264 return this.sanchors[this.position];
39267 getAlignAdj : function(){
39268 var cm = this.cmargins;
39269 switch(this.position){
39285 getExpandAdj : function(){
39286 var c = this.collapsedEl, cm = this.cmargins;
39287 switch(this.position){
39289 return [-(cm.right+c.getWidth()+cm.left), 0];
39292 return [cm.right+c.getWidth()+cm.left, 0];
39295 return [0, -(cm.top+cm.bottom+c.getHeight())];
39298 return [0, cm.top+cm.bottom+c.getHeight()];
39304 * Ext JS Library 1.1.1
39305 * Copyright(c) 2006-2007, Ext JS, LLC.
39307 * Originally Released Under LGPL - original licence link has changed is not relivant.
39310 * <script type="text/javascript">
39313 * These classes are private internal classes
39315 Roo.bootstrap.layout.Center = function(config){
39316 config.region = "center";
39317 Roo.bootstrap.layout.Region.call(this, config);
39318 this.visible = true;
39319 this.minWidth = config.minWidth || 20;
39320 this.minHeight = config.minHeight || 20;
39323 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39325 // center panel can't be hidden
39329 // center panel can't be hidden
39332 getMinWidth: function(){
39333 return this.minWidth;
39336 getMinHeight: function(){
39337 return this.minHeight;
39351 Roo.bootstrap.layout.North = function(config)
39353 config.region = 'north';
39354 config.cursor = 'n-resize';
39356 Roo.bootstrap.layout.Split.call(this, config);
39360 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39361 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39362 this.split.el.addClass("roo-layout-split-v");
39364 //var size = config.initialSize || config.height;
39365 //if(this.el && typeof size != "undefined"){
39366 // this.el.setHeight(size);
39369 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39371 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39374 onRender : function(ctr, pos)
39376 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39377 var size = this.config.initialSize || this.config.height;
39378 if(this.el && typeof size != "undefined"){
39379 this.el.setHeight(size);
39384 getBox : function(){
39385 if(this.collapsed){
39386 return this.collapsedEl.getBox();
39388 var box = this.el.getBox();
39390 box.height += this.split.el.getHeight();
39395 updateBox : function(box){
39396 if(this.split && !this.collapsed){
39397 box.height -= this.split.el.getHeight();
39398 this.split.el.setLeft(box.x);
39399 this.split.el.setTop(box.y+box.height);
39400 this.split.el.setWidth(box.width);
39402 if(this.collapsed){
39403 this.updateBody(box.width, null);
39405 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39413 Roo.bootstrap.layout.South = function(config){
39414 config.region = 'south';
39415 config.cursor = 's-resize';
39416 Roo.bootstrap.layout.Split.call(this, config);
39418 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39419 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39420 this.split.el.addClass("roo-layout-split-v");
39425 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39426 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39428 onRender : function(ctr, pos)
39430 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39431 var size = this.config.initialSize || this.config.height;
39432 if(this.el && typeof size != "undefined"){
39433 this.el.setHeight(size);
39438 getBox : function(){
39439 if(this.collapsed){
39440 return this.collapsedEl.getBox();
39442 var box = this.el.getBox();
39444 var sh = this.split.el.getHeight();
39451 updateBox : function(box){
39452 if(this.split && !this.collapsed){
39453 var sh = this.split.el.getHeight();
39456 this.split.el.setLeft(box.x);
39457 this.split.el.setTop(box.y-sh);
39458 this.split.el.setWidth(box.width);
39460 if(this.collapsed){
39461 this.updateBody(box.width, null);
39463 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39467 Roo.bootstrap.layout.East = function(config){
39468 config.region = "east";
39469 config.cursor = "e-resize";
39470 Roo.bootstrap.layout.Split.call(this, config);
39472 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39473 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39474 this.split.el.addClass("roo-layout-split-h");
39478 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39479 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39481 onRender : function(ctr, pos)
39483 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39484 var size = this.config.initialSize || this.config.width;
39485 if(this.el && typeof size != "undefined"){
39486 this.el.setWidth(size);
39491 getBox : function(){
39492 if(this.collapsed){
39493 return this.collapsedEl.getBox();
39495 var box = this.el.getBox();
39497 var sw = this.split.el.getWidth();
39504 updateBox : function(box){
39505 if(this.split && !this.collapsed){
39506 var sw = this.split.el.getWidth();
39508 this.split.el.setLeft(box.x);
39509 this.split.el.setTop(box.y);
39510 this.split.el.setHeight(box.height);
39513 if(this.collapsed){
39514 this.updateBody(null, box.height);
39516 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39520 Roo.bootstrap.layout.West = function(config){
39521 config.region = "west";
39522 config.cursor = "w-resize";
39524 Roo.bootstrap.layout.Split.call(this, config);
39526 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39527 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39528 this.split.el.addClass("roo-layout-split-h");
39532 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39533 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39535 onRender: function(ctr, pos)
39537 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39538 var size = this.config.initialSize || this.config.width;
39539 if(typeof size != "undefined"){
39540 this.el.setWidth(size);
39544 getBox : function(){
39545 if(this.collapsed){
39546 return this.collapsedEl.getBox();
39548 var box = this.el.getBox();
39549 if (box.width == 0) {
39550 box.width = this.config.width; // kludge?
39553 box.width += this.split.el.getWidth();
39558 updateBox : function(box){
39559 if(this.split && !this.collapsed){
39560 var sw = this.split.el.getWidth();
39562 this.split.el.setLeft(box.x+box.width);
39563 this.split.el.setTop(box.y);
39564 this.split.el.setHeight(box.height);
39566 if(this.collapsed){
39567 this.updateBody(null, box.height);
39569 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39571 });Roo.namespace("Roo.bootstrap.panel");/*
39573 * Ext JS Library 1.1.1
39574 * Copyright(c) 2006-2007, Ext JS, LLC.
39576 * Originally Released Under LGPL - original licence link has changed is not relivant.
39579 * <script type="text/javascript">
39582 * @class Roo.ContentPanel
39583 * @extends Roo.util.Observable
39584 * A basic ContentPanel element.
39585 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39586 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39587 * @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
39588 * @cfg {Boolean} closable True if the panel can be closed/removed
39589 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39590 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39591 * @cfg {Toolbar} toolbar A toolbar for this panel
39592 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39593 * @cfg {String} title The title for this panel
39594 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39595 * @cfg {String} url Calls {@link #setUrl} with this value
39596 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39597 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39598 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39599 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39600 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39601 * @cfg {Boolean} badges render the badges
39602 * @cfg {String} cls extra classes to use
39603 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39606 * Create a new ContentPanel.
39607 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39608 * @param {String/Object} config A string to set only the title or a config object
39609 * @param {String} content (optional) Set the HTML content for this panel
39610 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39612 Roo.bootstrap.panel.Content = function( config){
39614 this.tpl = config.tpl || false;
39616 var el = config.el;
39617 var content = config.content;
39619 if(config.autoCreate){ // xtype is available if this is called from factory
39622 this.el = Roo.get(el);
39623 if(!this.el && config && config.autoCreate){
39624 if(typeof config.autoCreate == "object"){
39625 if(!config.autoCreate.id){
39626 config.autoCreate.id = config.id||el;
39628 this.el = Roo.DomHelper.append(document.body,
39629 config.autoCreate, true);
39633 cls: (config.cls || '') +
39634 (config.background ? ' bg-' + config.background : '') +
39635 " roo-layout-inactive-content",
39638 if (config.iframe) {
39642 style : 'border: 0px',
39643 src : 'about:blank'
39649 elcfg.html = config.html;
39653 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39654 if (config.iframe) {
39655 this.iframeEl = this.el.select('iframe',true).first();
39660 this.closable = false;
39661 this.loaded = false;
39662 this.active = false;
39665 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39667 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39669 this.wrapEl = this.el; //this.el.wrap();
39671 if (config.toolbar.items) {
39672 ti = config.toolbar.items ;
39673 delete config.toolbar.items ;
39677 this.toolbar.render(this.wrapEl, 'before');
39678 for(var i =0;i < ti.length;i++) {
39679 // Roo.log(['add child', items[i]]);
39680 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39682 this.toolbar.items = nitems;
39683 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39684 delete config.toolbar;
39688 // xtype created footer. - not sure if will work as we normally have to render first..
39689 if (this.footer && !this.footer.el && this.footer.xtype) {
39690 if (!this.wrapEl) {
39691 this.wrapEl = this.el.wrap();
39694 this.footer.container = this.wrapEl.createChild();
39696 this.footer = Roo.factory(this.footer, Roo);
39701 if(typeof config == "string"){
39702 this.title = config;
39704 Roo.apply(this, config);
39708 this.resizeEl = Roo.get(this.resizeEl, true);
39710 this.resizeEl = this.el;
39712 // handle view.xtype
39720 * Fires when this panel is activated.
39721 * @param {Roo.ContentPanel} this
39725 * @event deactivate
39726 * Fires when this panel is activated.
39727 * @param {Roo.ContentPanel} this
39729 "deactivate" : true,
39733 * Fires when this panel is resized if fitToFrame is true.
39734 * @param {Roo.ContentPanel} this
39735 * @param {Number} width The width after any component adjustments
39736 * @param {Number} height The height after any component adjustments
39742 * Fires when this tab is created
39743 * @param {Roo.ContentPanel} this
39754 if(this.autoScroll && !this.iframe){
39755 this.resizeEl.setStyle("overflow", "auto");
39757 // fix randome scrolling
39758 //this.el.on('scroll', function() {
39759 // Roo.log('fix random scolling');
39760 // this.scrollTo('top',0);
39763 content = content || this.content;
39765 this.setContent(content);
39767 if(config && config.url){
39768 this.setUrl(this.url, this.params, this.loadOnce);
39773 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39775 if (this.view && typeof(this.view.xtype) != 'undefined') {
39776 this.view.el = this.el.appendChild(document.createElement("div"));
39777 this.view = Roo.factory(this.view);
39778 this.view.render && this.view.render(false, '');
39782 this.fireEvent('render', this);
39785 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39795 setRegion : function(region){
39796 this.region = region;
39797 this.setActiveClass(region && !this.background);
39801 setActiveClass: function(state)
39804 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39805 this.el.setStyle('position','relative');
39807 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39808 this.el.setStyle('position', 'absolute');
39813 * Returns the toolbar for this Panel if one was configured.
39814 * @return {Roo.Toolbar}
39816 getToolbar : function(){
39817 return this.toolbar;
39820 setActiveState : function(active)
39822 this.active = active;
39823 this.setActiveClass(active);
39825 if(this.fireEvent("deactivate", this) === false){
39830 this.fireEvent("activate", this);
39834 * Updates this panel's element (not for iframe)
39835 * @param {String} content The new content
39836 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39838 setContent : function(content, loadScripts){
39843 this.el.update(content, loadScripts);
39846 ignoreResize : function(w, h){
39847 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39850 this.lastSize = {width: w, height: h};
39855 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39856 * @return {Roo.UpdateManager} The UpdateManager
39858 getUpdateManager : function(){
39862 return this.el.getUpdateManager();
39865 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39866 * Does not work with IFRAME contents
39867 * @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:
39870 url: "your-url.php",
39871 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39872 callback: yourFunction,
39873 scope: yourObject, //(optional scope)
39876 text: "Loading...",
39882 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39883 * 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.
39884 * @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}
39885 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39886 * @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.
39887 * @return {Roo.ContentPanel} this
39895 var um = this.el.getUpdateManager();
39896 um.update.apply(um, arguments);
39902 * 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.
39903 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39904 * @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)
39905 * @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)
39906 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39908 setUrl : function(url, params, loadOnce){
39910 this.iframeEl.dom.src = url;
39914 if(this.refreshDelegate){
39915 this.removeListener("activate", this.refreshDelegate);
39917 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39918 this.on("activate", this.refreshDelegate);
39919 return this.el.getUpdateManager();
39922 _handleRefresh : function(url, params, loadOnce){
39923 if(!loadOnce || !this.loaded){
39924 var updater = this.el.getUpdateManager();
39925 updater.update(url, params, this._setLoaded.createDelegate(this));
39929 _setLoaded : function(){
39930 this.loaded = true;
39934 * Returns this panel's id
39937 getId : function(){
39942 * Returns this panel's element - used by regiosn to add.
39943 * @return {Roo.Element}
39945 getEl : function(){
39946 return this.wrapEl || this.el;
39951 adjustForComponents : function(width, height)
39953 //Roo.log('adjustForComponents ');
39954 if(this.resizeEl != this.el){
39955 width -= this.el.getFrameWidth('lr');
39956 height -= this.el.getFrameWidth('tb');
39959 var te = this.toolbar.getEl();
39960 te.setWidth(width);
39961 height -= te.getHeight();
39964 var te = this.footer.getEl();
39965 te.setWidth(width);
39966 height -= te.getHeight();
39970 if(this.adjustments){
39971 width += this.adjustments[0];
39972 height += this.adjustments[1];
39974 return {"width": width, "height": height};
39977 setSize : function(width, height){
39978 if(this.fitToFrame && !this.ignoreResize(width, height)){
39979 if(this.fitContainer && this.resizeEl != this.el){
39980 this.el.setSize(width, height);
39982 var size = this.adjustForComponents(width, height);
39984 this.iframeEl.setSize(width,height);
39987 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39988 this.fireEvent('resize', this, size.width, size.height);
39995 * Returns this panel's title
39998 getTitle : function(){
40000 if (typeof(this.title) != 'object') {
40005 for (var k in this.title) {
40006 if (!this.title.hasOwnProperty(k)) {
40010 if (k.indexOf('-') >= 0) {
40011 var s = k.split('-');
40012 for (var i = 0; i<s.length; i++) {
40013 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40016 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40023 * Set this panel's title
40024 * @param {String} title
40026 setTitle : function(title){
40027 this.title = title;
40029 this.region.updatePanelTitle(this, title);
40034 * Returns true is this panel was configured to be closable
40035 * @return {Boolean}
40037 isClosable : function(){
40038 return this.closable;
40041 beforeSlide : function(){
40043 this.resizeEl.clip();
40046 afterSlide : function(){
40048 this.resizeEl.unclip();
40052 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40053 * Will fail silently if the {@link #setUrl} method has not been called.
40054 * This does not activate the panel, just updates its content.
40056 refresh : function(){
40057 if(this.refreshDelegate){
40058 this.loaded = false;
40059 this.refreshDelegate();
40064 * Destroys this panel
40066 destroy : function(){
40067 this.el.removeAllListeners();
40068 var tempEl = document.createElement("span");
40069 tempEl.appendChild(this.el.dom);
40070 tempEl.innerHTML = "";
40076 * form - if the content panel contains a form - this is a reference to it.
40077 * @type {Roo.form.Form}
40081 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40082 * This contains a reference to it.
40088 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40098 * @param {Object} cfg Xtype definition of item to add.
40102 getChildContainer: function () {
40103 return this.getEl();
40108 var ret = new Roo.factory(cfg);
40113 if (cfg.xtype.match(/^Form$/)) {
40116 //if (this.footer) {
40117 // el = this.footer.container.insertSibling(false, 'before');
40119 el = this.el.createChild();
40122 this.form = new Roo.form.Form(cfg);
40125 if ( this.form.allItems.length) {
40126 this.form.render(el.dom);
40130 // should only have one of theses..
40131 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40132 // views.. should not be just added - used named prop 'view''
40134 cfg.el = this.el.appendChild(document.createElement("div"));
40137 var ret = new Roo.factory(cfg);
40139 ret.render && ret.render(false, ''); // render blank..
40149 * @class Roo.bootstrap.panel.Grid
40150 * @extends Roo.bootstrap.panel.Content
40152 * Create a new GridPanel.
40153 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40154 * @param {Object} config A the config object
40160 Roo.bootstrap.panel.Grid = function(config)
40164 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40165 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40167 config.el = this.wrapper;
40168 //this.el = this.wrapper;
40170 if (config.container) {
40171 // ctor'ed from a Border/panel.grid
40174 this.wrapper.setStyle("overflow", "hidden");
40175 this.wrapper.addClass('roo-grid-container');
40180 if(config.toolbar){
40181 var tool_el = this.wrapper.createChild();
40182 this.toolbar = Roo.factory(config.toolbar);
40184 if (config.toolbar.items) {
40185 ti = config.toolbar.items ;
40186 delete config.toolbar.items ;
40190 this.toolbar.render(tool_el);
40191 for(var i =0;i < ti.length;i++) {
40192 // Roo.log(['add child', items[i]]);
40193 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40195 this.toolbar.items = nitems;
40197 delete config.toolbar;
40200 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40201 config.grid.scrollBody = true;;
40202 config.grid.monitorWindowResize = false; // turn off autosizing
40203 config.grid.autoHeight = false;
40204 config.grid.autoWidth = false;
40206 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40208 if (config.background) {
40209 // render grid on panel activation (if panel background)
40210 this.on('activate', function(gp) {
40211 if (!gp.grid.rendered) {
40212 gp.grid.render(this.wrapper);
40213 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40218 this.grid.render(this.wrapper);
40219 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40222 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40223 // ??? needed ??? config.el = this.wrapper;
40228 // xtype created footer. - not sure if will work as we normally have to render first..
40229 if (this.footer && !this.footer.el && this.footer.xtype) {
40231 var ctr = this.grid.getView().getFooterPanel(true);
40232 this.footer.dataSource = this.grid.dataSource;
40233 this.footer = Roo.factory(this.footer, Roo);
40234 this.footer.render(ctr);
40244 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40245 getId : function(){
40246 return this.grid.id;
40250 * Returns the grid for this panel
40251 * @return {Roo.bootstrap.Table}
40253 getGrid : function(){
40257 setSize : function(width, height){
40258 if(!this.ignoreResize(width, height)){
40259 var grid = this.grid;
40260 var size = this.adjustForComponents(width, height);
40261 // tfoot is not a footer?
40264 var gridel = grid.getGridEl();
40265 gridel.setSize(size.width, size.height);
40267 var tbd = grid.getGridEl().select('tbody', true).first();
40268 var thd = grid.getGridEl().select('thead',true).first();
40269 var tbf= grid.getGridEl().select('tfoot', true).first();
40272 size.height -= tbf.getHeight();
40275 size.height -= thd.getHeight();
40278 tbd.setSize(size.width, size.height );
40279 // this is for the account management tab -seems to work there.
40280 var thd = grid.getGridEl().select('thead',true).first();
40282 // tbd.setSize(size.width, size.height - thd.getHeight());
40291 beforeSlide : function(){
40292 this.grid.getView().scroller.clip();
40295 afterSlide : function(){
40296 this.grid.getView().scroller.unclip();
40299 destroy : function(){
40300 this.grid.destroy();
40302 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40307 * @class Roo.bootstrap.panel.Nest
40308 * @extends Roo.bootstrap.panel.Content
40310 * Create a new Panel, that can contain a layout.Border.
40313 * @param {Roo.BorderLayout} layout The layout for this panel
40314 * @param {String/Object} config A string to set only the title or a config object
40316 Roo.bootstrap.panel.Nest = function(config)
40318 // construct with only one argument..
40319 /* FIXME - implement nicer consturctors
40320 if (layout.layout) {
40322 layout = config.layout;
40323 delete config.layout;
40325 if (layout.xtype && !layout.getEl) {
40326 // then layout needs constructing..
40327 layout = Roo.factory(layout, Roo);
40331 config.el = config.layout.getEl();
40333 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40335 config.layout.monitorWindowResize = false; // turn off autosizing
40336 this.layout = config.layout;
40337 this.layout.getEl().addClass("roo-layout-nested-layout");
40338 this.layout.parent = this;
40345 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40347 setSize : function(width, height){
40348 if(!this.ignoreResize(width, height)){
40349 var size = this.adjustForComponents(width, height);
40350 var el = this.layout.getEl();
40351 if (size.height < 1) {
40352 el.setWidth(size.width);
40354 el.setSize(size.width, size.height);
40356 var touch = el.dom.offsetWidth;
40357 this.layout.layout();
40358 // ie requires a double layout on the first pass
40359 if(Roo.isIE && !this.initialized){
40360 this.initialized = true;
40361 this.layout.layout();
40366 // activate all subpanels if not currently active..
40368 setActiveState : function(active){
40369 this.active = active;
40370 this.setActiveClass(active);
40373 this.fireEvent("deactivate", this);
40377 this.fireEvent("activate", this);
40378 // not sure if this should happen before or after..
40379 if (!this.layout) {
40380 return; // should not happen..
40383 for (var r in this.layout.regions) {
40384 reg = this.layout.getRegion(r);
40385 if (reg.getActivePanel()) {
40386 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40387 reg.setActivePanel(reg.getActivePanel());
40390 if (!reg.panels.length) {
40393 reg.showPanel(reg.getPanel(0));
40402 * Returns the nested BorderLayout for this panel
40403 * @return {Roo.BorderLayout}
40405 getLayout : function(){
40406 return this.layout;
40410 * Adds a xtype elements to the layout of the nested panel
40414 xtype : 'ContentPanel',
40421 xtype : 'NestedLayoutPanel',
40427 items : [ ... list of content panels or nested layout panels.. ]
40431 * @param {Object} cfg Xtype definition of item to add.
40433 addxtype : function(cfg) {
40434 return this.layout.addxtype(cfg);
40439 * Ext JS Library 1.1.1
40440 * Copyright(c) 2006-2007, Ext JS, LLC.
40442 * Originally Released Under LGPL - original licence link has changed is not relivant.
40445 * <script type="text/javascript">
40448 * @class Roo.TabPanel
40449 * @extends Roo.util.Observable
40450 * A lightweight tab container.
40454 // basic tabs 1, built from existing content
40455 var tabs = new Roo.TabPanel("tabs1");
40456 tabs.addTab("script", "View Script");
40457 tabs.addTab("markup", "View Markup");
40458 tabs.activate("script");
40460 // more advanced tabs, built from javascript
40461 var jtabs = new Roo.TabPanel("jtabs");
40462 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40464 // set up the UpdateManager
40465 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40466 var updater = tab2.getUpdateManager();
40467 updater.setDefaultUrl("ajax1.htm");
40468 tab2.on('activate', updater.refresh, updater, true);
40470 // Use setUrl for Ajax loading
40471 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40472 tab3.setUrl("ajax2.htm", null, true);
40475 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40478 jtabs.activate("jtabs-1");
40481 * Create a new TabPanel.
40482 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40483 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40485 Roo.bootstrap.panel.Tabs = function(config){
40487 * The container element for this TabPanel.
40488 * @type Roo.Element
40490 this.el = Roo.get(config.el);
40493 if(typeof config == "boolean"){
40494 this.tabPosition = config ? "bottom" : "top";
40496 Roo.apply(this, config);
40500 if(this.tabPosition == "bottom"){
40501 // if tabs are at the bottom = create the body first.
40502 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40503 this.el.addClass("roo-tabs-bottom");
40505 // next create the tabs holders
40507 if (this.tabPosition == "west"){
40509 var reg = this.region; // fake it..
40511 if (!reg.mgr.parent) {
40514 reg = reg.mgr.parent.region;
40516 Roo.log("got nest?");
40518 if (reg.mgr.getRegion('west')) {
40519 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40520 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40521 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40522 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40523 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40531 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40532 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40533 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40534 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40539 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40542 // finally - if tabs are at the top, then create the body last..
40543 if(this.tabPosition != "bottom"){
40544 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40545 * @type Roo.Element
40547 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40548 this.el.addClass("roo-tabs-top");
40552 this.bodyEl.setStyle("position", "relative");
40554 this.active = null;
40555 this.activateDelegate = this.activate.createDelegate(this);
40560 * Fires when the active tab changes
40561 * @param {Roo.TabPanel} this
40562 * @param {Roo.TabPanelItem} activePanel The new active tab
40566 * @event beforetabchange
40567 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40568 * @param {Roo.TabPanel} this
40569 * @param {Object} e Set cancel to true on this object to cancel the tab change
40570 * @param {Roo.TabPanelItem} tab The tab being changed to
40572 "beforetabchange" : true
40575 Roo.EventManager.onWindowResize(this.onResize, this);
40576 this.cpad = this.el.getPadding("lr");
40577 this.hiddenCount = 0;
40580 // toolbar on the tabbar support...
40581 if (this.toolbar) {
40582 alert("no toolbar support yet");
40583 this.toolbar = false;
40585 var tcfg = this.toolbar;
40586 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40587 this.toolbar = new Roo.Toolbar(tcfg);
40588 if (Roo.isSafari) {
40589 var tbl = tcfg.container.child('table', true);
40590 tbl.setAttribute('width', '100%');
40598 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40601 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40603 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40605 tabPosition : "top",
40607 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40609 currentTabWidth : 0,
40611 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40615 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40619 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40621 preferredTabWidth : 175,
40623 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40625 resizeTabs : false,
40627 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40629 monitorResize : true,
40631 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40633 toolbar : false, // set by caller..
40635 region : false, /// set by caller
40637 disableTooltips : true, // not used yet...
40640 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40641 * @param {String} id The id of the div to use <b>or create</b>
40642 * @param {String} text The text for the tab
40643 * @param {String} content (optional) Content to put in the TabPanelItem body
40644 * @param {Boolean} closable (optional) True to create a close icon on the tab
40645 * @return {Roo.TabPanelItem} The created TabPanelItem
40647 addTab : function(id, text, content, closable, tpl)
40649 var item = new Roo.bootstrap.panel.TabItem({
40653 closable : closable,
40656 this.addTabItem(item);
40658 item.setContent(content);
40664 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40665 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40666 * @return {Roo.TabPanelItem}
40668 getTab : function(id){
40669 return this.items[id];
40673 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40674 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40676 hideTab : function(id){
40677 var t = this.items[id];
40680 this.hiddenCount++;
40681 this.autoSizeTabs();
40686 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40687 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40689 unhideTab : function(id){
40690 var t = this.items[id];
40692 t.setHidden(false);
40693 this.hiddenCount--;
40694 this.autoSizeTabs();
40699 * Adds an existing {@link Roo.TabPanelItem}.
40700 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40702 addTabItem : function(item)
40704 this.items[item.id] = item;
40705 this.items.push(item);
40706 this.autoSizeTabs();
40707 // if(this.resizeTabs){
40708 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40709 // this.autoSizeTabs();
40711 // item.autoSize();
40716 * Removes a {@link Roo.TabPanelItem}.
40717 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40719 removeTab : function(id){
40720 var items = this.items;
40721 var tab = items[id];
40722 if(!tab) { return; }
40723 var index = items.indexOf(tab);
40724 if(this.active == tab && items.length > 1){
40725 var newTab = this.getNextAvailable(index);
40730 this.stripEl.dom.removeChild(tab.pnode.dom);
40731 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40732 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40734 items.splice(index, 1);
40735 delete this.items[tab.id];
40736 tab.fireEvent("close", tab);
40737 tab.purgeListeners();
40738 this.autoSizeTabs();
40741 getNextAvailable : function(start){
40742 var items = this.items;
40744 // look for a next tab that will slide over to
40745 // replace the one being removed
40746 while(index < items.length){
40747 var item = items[++index];
40748 if(item && !item.isHidden()){
40752 // if one isn't found select the previous tab (on the left)
40755 var item = items[--index];
40756 if(item && !item.isHidden()){
40764 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40765 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40767 disableTab : function(id){
40768 var tab = this.items[id];
40769 if(tab && this.active != tab){
40775 * Enables a {@link Roo.TabPanelItem} that is disabled.
40776 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40778 enableTab : function(id){
40779 var tab = this.items[id];
40784 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40785 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40786 * @return {Roo.TabPanelItem} The TabPanelItem.
40788 activate : function(id)
40790 //Roo.log('activite:' + id);
40792 var tab = this.items[id];
40796 if(tab == this.active || tab.disabled){
40800 this.fireEvent("beforetabchange", this, e, tab);
40801 if(e.cancel !== true && !tab.disabled){
40803 this.active.hide();
40805 this.active = this.items[id];
40806 this.active.show();
40807 this.fireEvent("tabchange", this, this.active);
40813 * Gets the active {@link Roo.TabPanelItem}.
40814 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40816 getActiveTab : function(){
40817 return this.active;
40821 * Updates the tab body element to fit the height of the container element
40822 * for overflow scrolling
40823 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40825 syncHeight : function(targetHeight){
40826 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40827 var bm = this.bodyEl.getMargins();
40828 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40829 this.bodyEl.setHeight(newHeight);
40833 onResize : function(){
40834 if(this.monitorResize){
40835 this.autoSizeTabs();
40840 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40842 beginUpdate : function(){
40843 this.updating = true;
40847 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40849 endUpdate : function(){
40850 this.updating = false;
40851 this.autoSizeTabs();
40855 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40857 autoSizeTabs : function()
40859 var count = this.items.length;
40860 var vcount = count - this.hiddenCount;
40863 this.stripEl.hide();
40865 this.stripEl.show();
40868 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40873 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40874 var availWidth = Math.floor(w / vcount);
40875 var b = this.stripBody;
40876 if(b.getWidth() > w){
40877 var tabs = this.items;
40878 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40879 if(availWidth < this.minTabWidth){
40880 /*if(!this.sleft){ // incomplete scrolling code
40881 this.createScrollButtons();
40884 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40887 if(this.currentTabWidth < this.preferredTabWidth){
40888 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40894 * Returns the number of tabs in this TabPanel.
40897 getCount : function(){
40898 return this.items.length;
40902 * Resizes all the tabs to the passed width
40903 * @param {Number} The new width
40905 setTabWidth : function(width){
40906 this.currentTabWidth = width;
40907 for(var i = 0, len = this.items.length; i < len; i++) {
40908 if(!this.items[i].isHidden()) {
40909 this.items[i].setWidth(width);
40915 * Destroys this TabPanel
40916 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40918 destroy : function(removeEl){
40919 Roo.EventManager.removeResizeListener(this.onResize, this);
40920 for(var i = 0, len = this.items.length; i < len; i++){
40921 this.items[i].purgeListeners();
40923 if(removeEl === true){
40924 this.el.update("");
40929 createStrip : function(container)
40931 var strip = document.createElement("nav");
40932 strip.className = Roo.bootstrap.version == 4 ?
40933 "navbar-light bg-light" :
40934 "navbar navbar-default"; //"x-tabs-wrap";
40935 container.appendChild(strip);
40939 createStripList : function(strip)
40941 // div wrapper for retard IE
40942 // returns the "tr" element.
40943 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40944 //'<div class="x-tabs-strip-wrap">'+
40945 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40946 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40947 return strip.firstChild; //.firstChild.firstChild.firstChild;
40949 createBody : function(container)
40951 var body = document.createElement("div");
40952 Roo.id(body, "tab-body");
40953 //Roo.fly(body).addClass("x-tabs-body");
40954 Roo.fly(body).addClass("tab-content");
40955 container.appendChild(body);
40958 createItemBody :function(bodyEl, id){
40959 var body = Roo.getDom(id);
40961 body = document.createElement("div");
40964 //Roo.fly(body).addClass("x-tabs-item-body");
40965 Roo.fly(body).addClass("tab-pane");
40966 bodyEl.insertBefore(body, bodyEl.firstChild);
40970 createStripElements : function(stripEl, text, closable, tpl)
40972 var td = document.createElement("li"); // was td..
40973 td.className = 'nav-item';
40975 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40978 stripEl.appendChild(td);
40980 td.className = "x-tabs-closable";
40981 if(!this.closeTpl){
40982 this.closeTpl = new Roo.Template(
40983 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40984 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40985 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40988 var el = this.closeTpl.overwrite(td, {"text": text});
40989 var close = el.getElementsByTagName("div")[0];
40990 var inner = el.getElementsByTagName("em")[0];
40991 return {"el": el, "close": close, "inner": inner};
40994 // not sure what this is..
40995 // if(!this.tabTpl){
40996 //this.tabTpl = new Roo.Template(
40997 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40998 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41000 // this.tabTpl = new Roo.Template(
41001 // '<a href="#">' +
41002 // '<span unselectable="on"' +
41003 // (this.disableTooltips ? '' : ' title="{text}"') +
41004 // ' >{text}</span></a>'
41010 var template = tpl || this.tabTpl || false;
41013 template = new Roo.Template(
41014 Roo.bootstrap.version == 4 ?
41016 '<a class="nav-link" href="#" unselectable="on"' +
41017 (this.disableTooltips ? '' : ' title="{text}"') +
41020 '<a class="nav-link" href="#">' +
41021 '<span unselectable="on"' +
41022 (this.disableTooltips ? '' : ' title="{text}"') +
41023 ' >{text}</span></a>'
41028 switch (typeof(template)) {
41032 template = new Roo.Template(template);
41038 var el = template.overwrite(td, {"text": text});
41040 var inner = el.getElementsByTagName("span")[0];
41042 return {"el": el, "inner": inner};
41050 * @class Roo.TabPanelItem
41051 * @extends Roo.util.Observable
41052 * Represents an individual item (tab plus body) in a TabPanel.
41053 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41054 * @param {String} id The id of this TabPanelItem
41055 * @param {String} text The text for the tab of this TabPanelItem
41056 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41058 Roo.bootstrap.panel.TabItem = function(config){
41060 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41061 * @type Roo.TabPanel
41063 this.tabPanel = config.panel;
41065 * The id for this TabPanelItem
41068 this.id = config.id;
41070 this.disabled = false;
41072 this.text = config.text;
41074 this.loaded = false;
41075 this.closable = config.closable;
41078 * The body element for this TabPanelItem.
41079 * @type Roo.Element
41081 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41082 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41083 this.bodyEl.setStyle("display", "block");
41084 this.bodyEl.setStyle("zoom", "1");
41085 //this.hideAction();
41087 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41089 this.el = Roo.get(els.el);
41090 this.inner = Roo.get(els.inner, true);
41091 this.textEl = Roo.bootstrap.version == 4 ?
41092 this.el : Roo.get(this.el.dom.firstChild, true);
41094 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41095 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41098 // this.el.on("mousedown", this.onTabMouseDown, this);
41099 this.el.on("click", this.onTabClick, this);
41101 if(config.closable){
41102 var c = Roo.get(els.close, true);
41103 c.dom.title = this.closeText;
41104 c.addClassOnOver("close-over");
41105 c.on("click", this.closeClick, this);
41111 * Fires when this tab becomes the active tab.
41112 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41113 * @param {Roo.TabPanelItem} this
41117 * @event beforeclose
41118 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41119 * @param {Roo.TabPanelItem} this
41120 * @param {Object} e Set cancel to true on this object to cancel the close.
41122 "beforeclose": true,
41125 * Fires when this tab is closed.
41126 * @param {Roo.TabPanelItem} this
41130 * @event deactivate
41131 * Fires when this tab is no longer the active tab.
41132 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41133 * @param {Roo.TabPanelItem} this
41135 "deactivate" : true
41137 this.hidden = false;
41139 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41142 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41144 purgeListeners : function(){
41145 Roo.util.Observable.prototype.purgeListeners.call(this);
41146 this.el.removeAllListeners();
41149 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41152 this.status_node.addClass("active");
41155 this.tabPanel.stripWrap.repaint();
41157 this.fireEvent("activate", this.tabPanel, this);
41161 * Returns true if this tab is the active tab.
41162 * @return {Boolean}
41164 isActive : function(){
41165 return this.tabPanel.getActiveTab() == this;
41169 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41172 this.status_node.removeClass("active");
41174 this.fireEvent("deactivate", this.tabPanel, this);
41177 hideAction : function(){
41178 this.bodyEl.hide();
41179 this.bodyEl.setStyle("position", "absolute");
41180 this.bodyEl.setLeft("-20000px");
41181 this.bodyEl.setTop("-20000px");
41184 showAction : function(){
41185 this.bodyEl.setStyle("position", "relative");
41186 this.bodyEl.setTop("");
41187 this.bodyEl.setLeft("");
41188 this.bodyEl.show();
41192 * Set the tooltip for the tab.
41193 * @param {String} tooltip The tab's tooltip
41195 setTooltip : function(text){
41196 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41197 this.textEl.dom.qtip = text;
41198 this.textEl.dom.removeAttribute('title');
41200 this.textEl.dom.title = text;
41204 onTabClick : function(e){
41205 e.preventDefault();
41206 this.tabPanel.activate(this.id);
41209 onTabMouseDown : function(e){
41210 e.preventDefault();
41211 this.tabPanel.activate(this.id);
41214 getWidth : function(){
41215 return this.inner.getWidth();
41218 setWidth : function(width){
41219 var iwidth = width - this.linode.getPadding("lr");
41220 this.inner.setWidth(iwidth);
41221 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41222 this.linode.setWidth(width);
41226 * Show or hide the tab
41227 * @param {Boolean} hidden True to hide or false to show.
41229 setHidden : function(hidden){
41230 this.hidden = hidden;
41231 this.linode.setStyle("display", hidden ? "none" : "");
41235 * Returns true if this tab is "hidden"
41236 * @return {Boolean}
41238 isHidden : function(){
41239 return this.hidden;
41243 * Returns the text for this tab
41246 getText : function(){
41250 autoSize : function(){
41251 //this.el.beginMeasure();
41252 this.textEl.setWidth(1);
41254 * #2804 [new] Tabs in Roojs
41255 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41257 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41258 //this.el.endMeasure();
41262 * Sets the text for the tab (Note: this also sets the tooltip text)
41263 * @param {String} text The tab's text and tooltip
41265 setText : function(text){
41267 this.textEl.update(text);
41268 this.setTooltip(text);
41269 //if(!this.tabPanel.resizeTabs){
41270 // this.autoSize();
41274 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41276 activate : function(){
41277 this.tabPanel.activate(this.id);
41281 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41283 disable : function(){
41284 if(this.tabPanel.active != this){
41285 this.disabled = true;
41286 this.status_node.addClass("disabled");
41291 * Enables this TabPanelItem if it was previously disabled.
41293 enable : function(){
41294 this.disabled = false;
41295 this.status_node.removeClass("disabled");
41299 * Sets the content for this TabPanelItem.
41300 * @param {String} content The content
41301 * @param {Boolean} loadScripts true to look for and load scripts
41303 setContent : function(content, loadScripts){
41304 this.bodyEl.update(content, loadScripts);
41308 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41309 * @return {Roo.UpdateManager} The UpdateManager
41311 getUpdateManager : function(){
41312 return this.bodyEl.getUpdateManager();
41316 * Set a URL to be used to load the content for this TabPanelItem.
41317 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41318 * @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)
41319 * @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)
41320 * @return {Roo.UpdateManager} The UpdateManager
41322 setUrl : function(url, params, loadOnce){
41323 if(this.refreshDelegate){
41324 this.un('activate', this.refreshDelegate);
41326 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41327 this.on("activate", this.refreshDelegate);
41328 return this.bodyEl.getUpdateManager();
41332 _handleRefresh : function(url, params, loadOnce){
41333 if(!loadOnce || !this.loaded){
41334 var updater = this.bodyEl.getUpdateManager();
41335 updater.update(url, params, this._setLoaded.createDelegate(this));
41340 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41341 * Will fail silently if the setUrl method has not been called.
41342 * This does not activate the panel, just updates its content.
41344 refresh : function(){
41345 if(this.refreshDelegate){
41346 this.loaded = false;
41347 this.refreshDelegate();
41352 _setLoaded : function(){
41353 this.loaded = true;
41357 closeClick : function(e){
41360 this.fireEvent("beforeclose", this, o);
41361 if(o.cancel !== true){
41362 this.tabPanel.removeTab(this.id);
41366 * The text displayed in the tooltip for the close icon.
41369 closeText : "Close this tab"
41372 * This script refer to:
41373 * Title: International Telephone Input
41374 * Author: Jack O'Connor
41375 * Code version: v12.1.12
41376 * Availability: https://github.com/jackocnr/intl-tel-input.git
41379 Roo.bootstrap.PhoneInputData = function() {
41382 "Afghanistan (افغانستان)",
41387 "Albania (Shqipëri)",
41392 "Algeria (الجزائر)",
41417 "Antigua and Barbuda",
41427 "Armenia (Հայաստան)",
41443 "Austria (Österreich)",
41448 "Azerbaijan (Azərbaycan)",
41458 "Bahrain (البحرين)",
41463 "Bangladesh (বাংলাদেশ)",
41473 "Belarus (Беларусь)",
41478 "Belgium (België)",
41508 "Bosnia and Herzegovina (Босна и Херцеговина)",
41523 "British Indian Ocean Territory",
41528 "British Virgin Islands",
41538 "Bulgaria (България)",
41548 "Burundi (Uburundi)",
41553 "Cambodia (កម្ពុជា)",
41558 "Cameroon (Cameroun)",
41567 ["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"]
41570 "Cape Verde (Kabu Verdi)",
41575 "Caribbean Netherlands",
41586 "Central African Republic (République centrafricaine)",
41606 "Christmas Island",
41612 "Cocos (Keeling) Islands",
41623 "Comoros (جزر القمر)",
41628 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41633 "Congo (Republic) (Congo-Brazzaville)",
41653 "Croatia (Hrvatska)",
41674 "Czech Republic (Česká republika)",
41679 "Denmark (Danmark)",
41694 "Dominican Republic (República Dominicana)",
41698 ["809", "829", "849"]
41716 "Equatorial Guinea (Guinea Ecuatorial)",
41736 "Falkland Islands (Islas Malvinas)",
41741 "Faroe Islands (Føroyar)",
41762 "French Guiana (Guyane française)",
41767 "French Polynesia (Polynésie française)",
41782 "Georgia (საქართველო)",
41787 "Germany (Deutschland)",
41807 "Greenland (Kalaallit Nunaat)",
41844 "Guinea-Bissau (Guiné Bissau)",
41869 "Hungary (Magyarország)",
41874 "Iceland (Ísland)",
41894 "Iraq (العراق)",
41910 "Israel (ישראל)",
41937 "Jordan (الأردن)",
41942 "Kazakhstan (Казахстан)",
41963 "Kuwait (الكويت)",
41968 "Kyrgyzstan (Кыргызстан)",
41978 "Latvia (Latvija)",
41983 "Lebanon (لبنان)",
41998 "Libya (ليبيا)",
42008 "Lithuania (Lietuva)",
42023 "Macedonia (FYROM) (Македонија)",
42028 "Madagascar (Madagasikara)",
42058 "Marshall Islands",
42068 "Mauritania (موريتانيا)",
42073 "Mauritius (Moris)",
42094 "Moldova (Republica Moldova)",
42104 "Mongolia (Монгол)",
42109 "Montenegro (Crna Gora)",
42119 "Morocco (المغرب)",
42125 "Mozambique (Moçambique)",
42130 "Myanmar (Burma) (မြန်မာ)",
42135 "Namibia (Namibië)",
42150 "Netherlands (Nederland)",
42155 "New Caledonia (Nouvelle-Calédonie)",
42190 "North Korea (조선 민주주의 인민 공화국)",
42195 "Northern Mariana Islands",
42211 "Pakistan (پاکستان)",
42221 "Palestine (فلسطين)",
42231 "Papua New Guinea",
42273 "Réunion (La Réunion)",
42279 "Romania (România)",
42295 "Saint Barthélemy",
42306 "Saint Kitts and Nevis",
42316 "Saint Martin (Saint-Martin (partie française))",
42322 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42327 "Saint Vincent and the Grenadines",
42342 "São Tomé and Príncipe (São Tomé e Príncipe)",
42347 "Saudi Arabia (المملكة العربية السعودية)",
42352 "Senegal (Sénégal)",
42382 "Slovakia (Slovensko)",
42387 "Slovenia (Slovenija)",
42397 "Somalia (Soomaaliya)",
42407 "South Korea (대한민국)",
42412 "South Sudan (جنوب السودان)",
42422 "Sri Lanka (ශ්රී ලංකාව)",
42427 "Sudan (السودان)",
42437 "Svalbard and Jan Mayen",
42448 "Sweden (Sverige)",
42453 "Switzerland (Schweiz)",
42458 "Syria (سوريا)",
42503 "Trinidad and Tobago",
42508 "Tunisia (تونس)",
42513 "Turkey (Türkiye)",
42523 "Turks and Caicos Islands",
42533 "U.S. Virgin Islands",
42543 "Ukraine (Україна)",
42548 "United Arab Emirates (الإمارات العربية المتحدة)",
42570 "Uzbekistan (Oʻzbekiston)",
42580 "Vatican City (Città del Vaticano)",
42591 "Vietnam (Việt Nam)",
42596 "Wallis and Futuna (Wallis-et-Futuna)",
42601 "Western Sahara (الصحراء الغربية)",
42607 "Yemen (اليمن)",
42631 * This script refer to:
42632 * Title: International Telephone Input
42633 * Author: Jack O'Connor
42634 * Code version: v12.1.12
42635 * Availability: https://github.com/jackocnr/intl-tel-input.git
42639 * @class Roo.bootstrap.PhoneInput
42640 * @extends Roo.bootstrap.TriggerField
42641 * An input with International dial-code selection
42643 * @cfg {String} defaultDialCode default '+852'
42644 * @cfg {Array} preferedCountries default []
42647 * Create a new PhoneInput.
42648 * @param {Object} config Configuration options
42651 Roo.bootstrap.PhoneInput = function(config) {
42652 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42655 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42657 listWidth: undefined,
42659 selectedClass: 'active',
42661 invalidClass : "has-warning",
42663 validClass: 'has-success',
42665 allowed: '0123456789',
42670 * @cfg {String} defaultDialCode The default dial code when initializing the input
42672 defaultDialCode: '+852',
42675 * @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
42677 preferedCountries: false,
42679 getAutoCreate : function()
42681 var data = Roo.bootstrap.PhoneInputData();
42682 var align = this.labelAlign || this.parentLabelAlign();
42685 this.allCountries = [];
42686 this.dialCodeMapping = [];
42688 for (var i = 0; i < data.length; i++) {
42690 this.allCountries[i] = {
42694 priority: c[3] || 0,
42695 areaCodes: c[4] || null
42697 this.dialCodeMapping[c[2]] = {
42700 priority: c[3] || 0,
42701 areaCodes: c[4] || null
42713 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42714 maxlength: this.max_length,
42715 cls : 'form-control tel-input',
42716 autocomplete: 'new-password'
42719 var hiddenInput = {
42722 cls: 'hidden-tel-input'
42726 hiddenInput.name = this.name;
42729 if (this.disabled) {
42730 input.disabled = true;
42733 var flag_container = {
42750 cls: this.hasFeedback ? 'has-feedback' : '',
42756 cls: 'dial-code-holder',
42763 cls: 'roo-select2-container input-group',
42770 if (this.fieldLabel.length) {
42773 tooltip: 'This field is required'
42779 cls: 'control-label',
42785 html: this.fieldLabel
42788 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42794 if(this.indicatorpos == 'right') {
42795 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42802 if(align == 'left') {
42810 if(this.labelWidth > 12){
42811 label.style = "width: " + this.labelWidth + 'px';
42813 if(this.labelWidth < 13 && this.labelmd == 0){
42814 this.labelmd = this.labelWidth;
42816 if(this.labellg > 0){
42817 label.cls += ' col-lg-' + this.labellg;
42818 input.cls += ' col-lg-' + (12 - this.labellg);
42820 if(this.labelmd > 0){
42821 label.cls += ' col-md-' + this.labelmd;
42822 container.cls += ' col-md-' + (12 - this.labelmd);
42824 if(this.labelsm > 0){
42825 label.cls += ' col-sm-' + this.labelsm;
42826 container.cls += ' col-sm-' + (12 - this.labelsm);
42828 if(this.labelxs > 0){
42829 label.cls += ' col-xs-' + this.labelxs;
42830 container.cls += ' col-xs-' + (12 - this.labelxs);
42840 var settings = this;
42842 ['xs','sm','md','lg'].map(function(size){
42843 if (settings[size]) {
42844 cfg.cls += ' col-' + size + '-' + settings[size];
42848 this.store = new Roo.data.Store({
42849 proxy : new Roo.data.MemoryProxy({}),
42850 reader : new Roo.data.JsonReader({
42861 'name' : 'dialCode',
42865 'name' : 'priority',
42869 'name' : 'areaCodes',
42876 if(!this.preferedCountries) {
42877 this.preferedCountries = [
42884 var p = this.preferedCountries.reverse();
42887 for (var i = 0; i < p.length; i++) {
42888 for (var j = 0; j < this.allCountries.length; j++) {
42889 if(this.allCountries[j].iso2 == p[i]) {
42890 var t = this.allCountries[j];
42891 this.allCountries.splice(j,1);
42892 this.allCountries.unshift(t);
42898 this.store.proxy.data = {
42900 data: this.allCountries
42906 initEvents : function()
42909 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42911 this.indicator = this.indicatorEl();
42912 this.flag = this.flagEl();
42913 this.dialCodeHolder = this.dialCodeHolderEl();
42915 this.trigger = this.el.select('div.flag-box',true).first();
42916 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42921 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42922 _this.list.setWidth(lw);
42925 this.list.on('mouseover', this.onViewOver, this);
42926 this.list.on('mousemove', this.onViewMove, this);
42927 this.inputEl().on("keyup", this.onKeyUp, this);
42928 this.inputEl().on("keypress", this.onKeyPress, this);
42930 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42932 this.view = new Roo.View(this.list, this.tpl, {
42933 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42936 this.view.on('click', this.onViewClick, this);
42937 this.setValue(this.defaultDialCode);
42940 onTriggerClick : function(e)
42942 Roo.log('trigger click');
42947 if(this.isExpanded()){
42949 this.hasFocus = false;
42951 this.store.load({});
42952 this.hasFocus = true;
42957 isExpanded : function()
42959 return this.list.isVisible();
42962 collapse : function()
42964 if(!this.isExpanded()){
42968 Roo.get(document).un('mousedown', this.collapseIf, this);
42969 Roo.get(document).un('mousewheel', this.collapseIf, this);
42970 this.fireEvent('collapse', this);
42974 expand : function()
42978 if(this.isExpanded() || !this.hasFocus){
42982 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42983 this.list.setWidth(lw);
42986 this.restrictHeight();
42988 Roo.get(document).on('mousedown', this.collapseIf, this);
42989 Roo.get(document).on('mousewheel', this.collapseIf, this);
42991 this.fireEvent('expand', this);
42994 restrictHeight : function()
42996 this.list.alignTo(this.inputEl(), this.listAlign);
42997 this.list.alignTo(this.inputEl(), this.listAlign);
43000 onViewOver : function(e, t)
43002 if(this.inKeyMode){
43005 var item = this.view.findItemFromChild(t);
43008 var index = this.view.indexOf(item);
43009 this.select(index, false);
43014 onViewClick : function(view, doFocus, el, e)
43016 var index = this.view.getSelectedIndexes()[0];
43018 var r = this.store.getAt(index);
43021 this.onSelect(r, index);
43023 if(doFocus !== false && !this.blockFocus){
43024 this.inputEl().focus();
43028 onViewMove : function(e, t)
43030 this.inKeyMode = false;
43033 select : function(index, scrollIntoView)
43035 this.selectedIndex = index;
43036 this.view.select(index);
43037 if(scrollIntoView !== false){
43038 var el = this.view.getNode(index);
43040 this.list.scrollChildIntoView(el, false);
43045 createList : function()
43047 this.list = Roo.get(document.body).createChild({
43049 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43050 style: 'display:none'
43053 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43056 collapseIf : function(e)
43058 var in_combo = e.within(this.el);
43059 var in_list = e.within(this.list);
43060 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43062 if (in_combo || in_list || is_list) {
43068 onSelect : function(record, index)
43070 if(this.fireEvent('beforeselect', this, record, index) !== false){
43072 this.setFlagClass(record.data.iso2);
43073 this.setDialCode(record.data.dialCode);
43074 this.hasFocus = false;
43076 this.fireEvent('select', this, record, index);
43080 flagEl : function()
43082 var flag = this.el.select('div.flag',true).first();
43089 dialCodeHolderEl : function()
43091 var d = this.el.select('input.dial-code-holder',true).first();
43098 setDialCode : function(v)
43100 this.dialCodeHolder.dom.value = '+'+v;
43103 setFlagClass : function(n)
43105 this.flag.dom.className = 'flag '+n;
43108 getValue : function()
43110 var v = this.inputEl().getValue();
43111 if(this.dialCodeHolder) {
43112 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43117 setValue : function(v)
43119 var d = this.getDialCode(v);
43121 //invalid dial code
43122 if(v.length == 0 || !d || d.length == 0) {
43124 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43125 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43131 this.setFlagClass(this.dialCodeMapping[d].iso2);
43132 this.setDialCode(d);
43133 this.inputEl().dom.value = v.replace('+'+d,'');
43134 this.hiddenEl().dom.value = this.getValue();
43139 getDialCode : function(v)
43143 if (v.length == 0) {
43144 return this.dialCodeHolder.dom.value;
43148 if (v.charAt(0) != "+") {
43151 var numericChars = "";
43152 for (var i = 1; i < v.length; i++) {
43153 var c = v.charAt(i);
43156 if (this.dialCodeMapping[numericChars]) {
43157 dialCode = v.substr(1, i);
43159 if (numericChars.length == 4) {
43169 this.setValue(this.defaultDialCode);
43173 hiddenEl : function()
43175 return this.el.select('input.hidden-tel-input',true).first();
43178 // after setting val
43179 onKeyUp : function(e){
43180 this.setValue(this.getValue());
43183 onKeyPress : function(e){
43184 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43191 * @class Roo.bootstrap.MoneyField
43192 * @extends Roo.bootstrap.ComboBox
43193 * Bootstrap MoneyField class
43196 * Create a new MoneyField.
43197 * @param {Object} config Configuration options
43200 Roo.bootstrap.MoneyField = function(config) {
43202 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43206 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43209 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43211 allowDecimals : true,
43213 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43215 decimalSeparator : ".",
43217 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43219 decimalPrecision : 0,
43221 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43223 allowNegative : true,
43225 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43229 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43231 minValue : Number.NEGATIVE_INFINITY,
43233 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43235 maxValue : Number.MAX_VALUE,
43237 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43239 minText : "The minimum value for this field is {0}",
43241 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43243 maxText : "The maximum value for this field is {0}",
43245 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43246 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43248 nanText : "{0} is not a valid number",
43250 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43254 * @cfg {String} defaults currency of the MoneyField
43255 * value should be in lkey
43257 defaultCurrency : false,
43259 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43261 thousandsDelimiter : false,
43263 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43274 getAutoCreate : function()
43276 var align = this.labelAlign || this.parentLabelAlign();
43288 cls : 'form-control roo-money-amount-input',
43289 autocomplete: 'new-password'
43292 var hiddenInput = {
43296 cls: 'hidden-number-input'
43299 if(this.max_length) {
43300 input.maxlength = this.max_length;
43304 hiddenInput.name = this.name;
43307 if (this.disabled) {
43308 input.disabled = true;
43311 var clg = 12 - this.inputlg;
43312 var cmd = 12 - this.inputmd;
43313 var csm = 12 - this.inputsm;
43314 var cxs = 12 - this.inputxs;
43318 cls : 'row roo-money-field',
43322 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43326 cls: 'roo-select2-container input-group',
43330 cls : 'form-control roo-money-currency-input',
43331 autocomplete: 'new-password',
43333 name : this.currencyName
43337 cls : 'input-group-addon',
43351 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43355 cls: this.hasFeedback ? 'has-feedback' : '',
43366 if (this.fieldLabel.length) {
43369 tooltip: 'This field is required'
43375 cls: 'control-label',
43381 html: this.fieldLabel
43384 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43390 if(this.indicatorpos == 'right') {
43391 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43398 if(align == 'left') {
43406 if(this.labelWidth > 12){
43407 label.style = "width: " + this.labelWidth + 'px';
43409 if(this.labelWidth < 13 && this.labelmd == 0){
43410 this.labelmd = this.labelWidth;
43412 if(this.labellg > 0){
43413 label.cls += ' col-lg-' + this.labellg;
43414 input.cls += ' col-lg-' + (12 - this.labellg);
43416 if(this.labelmd > 0){
43417 label.cls += ' col-md-' + this.labelmd;
43418 container.cls += ' col-md-' + (12 - this.labelmd);
43420 if(this.labelsm > 0){
43421 label.cls += ' col-sm-' + this.labelsm;
43422 container.cls += ' col-sm-' + (12 - this.labelsm);
43424 if(this.labelxs > 0){
43425 label.cls += ' col-xs-' + this.labelxs;
43426 container.cls += ' col-xs-' + (12 - this.labelxs);
43437 var settings = this;
43439 ['xs','sm','md','lg'].map(function(size){
43440 if (settings[size]) {
43441 cfg.cls += ' col-' + size + '-' + settings[size];
43448 initEvents : function()
43450 this.indicator = this.indicatorEl();
43452 this.initCurrencyEvent();
43454 this.initNumberEvent();
43457 initCurrencyEvent : function()
43460 throw "can not find store for combo";
43463 this.store = Roo.factory(this.store, Roo.data);
43464 this.store.parent = this;
43468 this.triggerEl = this.el.select('.input-group-addon', true).first();
43470 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43475 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43476 _this.list.setWidth(lw);
43479 this.list.on('mouseover', this.onViewOver, this);
43480 this.list.on('mousemove', this.onViewMove, this);
43481 this.list.on('scroll', this.onViewScroll, this);
43484 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43487 this.view = new Roo.View(this.list, this.tpl, {
43488 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43491 this.view.on('click', this.onViewClick, this);
43493 this.store.on('beforeload', this.onBeforeLoad, this);
43494 this.store.on('load', this.onLoad, this);
43495 this.store.on('loadexception', this.onLoadException, this);
43497 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43498 "up" : function(e){
43499 this.inKeyMode = true;
43503 "down" : function(e){
43504 if(!this.isExpanded()){
43505 this.onTriggerClick();
43507 this.inKeyMode = true;
43512 "enter" : function(e){
43515 if(this.fireEvent("specialkey", this, e)){
43516 this.onViewClick(false);
43522 "esc" : function(e){
43526 "tab" : function(e){
43529 if(this.fireEvent("specialkey", this, e)){
43530 this.onViewClick(false);
43538 doRelay : function(foo, bar, hname){
43539 if(hname == 'down' || this.scope.isExpanded()){
43540 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43548 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43552 initNumberEvent : function(e)
43554 this.inputEl().on("keydown" , this.fireKey, this);
43555 this.inputEl().on("focus", this.onFocus, this);
43556 this.inputEl().on("blur", this.onBlur, this);
43558 this.inputEl().relayEvent('keyup', this);
43560 if(this.indicator){
43561 this.indicator.addClass('invisible');
43564 this.originalValue = this.getValue();
43566 if(this.validationEvent == 'keyup'){
43567 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43568 this.inputEl().on('keyup', this.filterValidation, this);
43570 else if(this.validationEvent !== false){
43571 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43574 if(this.selectOnFocus){
43575 this.on("focus", this.preFocus, this);
43578 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43579 this.inputEl().on("keypress", this.filterKeys, this);
43581 this.inputEl().relayEvent('keypress', this);
43584 var allowed = "0123456789";
43586 if(this.allowDecimals){
43587 allowed += this.decimalSeparator;
43590 if(this.allowNegative){
43594 if(this.thousandsDelimiter) {
43598 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43600 var keyPress = function(e){
43602 var k = e.getKey();
43604 var c = e.getCharCode();
43607 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43608 allowed.indexOf(String.fromCharCode(c)) === -1
43614 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43618 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43623 this.inputEl().on("keypress", keyPress, this);
43627 onTriggerClick : function(e)
43634 this.loadNext = false;
43636 if(this.isExpanded()){
43641 this.hasFocus = true;
43643 if(this.triggerAction == 'all') {
43644 this.doQuery(this.allQuery, true);
43648 this.doQuery(this.getRawValue());
43651 getCurrency : function()
43653 var v = this.currencyEl().getValue();
43658 restrictHeight : function()
43660 this.list.alignTo(this.currencyEl(), this.listAlign);
43661 this.list.alignTo(this.currencyEl(), this.listAlign);
43664 onViewClick : function(view, doFocus, el, e)
43666 var index = this.view.getSelectedIndexes()[0];
43668 var r = this.store.getAt(index);
43671 this.onSelect(r, index);
43675 onSelect : function(record, index){
43677 if(this.fireEvent('beforeselect', this, record, index) !== false){
43679 this.setFromCurrencyData(index > -1 ? record.data : false);
43683 this.fireEvent('select', this, record, index);
43687 setFromCurrencyData : function(o)
43691 this.lastCurrency = o;
43693 if (this.currencyField) {
43694 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43696 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43699 this.lastSelectionText = currency;
43701 //setting default currency
43702 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43703 this.setCurrency(this.defaultCurrency);
43707 this.setCurrency(currency);
43710 setFromData : function(o)
43714 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43716 this.setFromCurrencyData(c);
43721 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43723 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43726 this.setValue(value);
43730 setCurrency : function(v)
43732 this.currencyValue = v;
43735 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43740 setValue : function(v)
43742 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43748 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43750 this.inputEl().dom.value = (v == '') ? '' :
43751 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43753 if(!this.allowZero && v === '0') {
43754 this.hiddenEl().dom.value = '';
43755 this.inputEl().dom.value = '';
43762 getRawValue : function()
43764 var v = this.inputEl().getValue();
43769 getValue : function()
43771 return this.fixPrecision(this.parseValue(this.getRawValue()));
43774 parseValue : function(value)
43776 if(this.thousandsDelimiter) {
43778 r = new RegExp(",", "g");
43779 value = value.replace(r, "");
43782 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43783 return isNaN(value) ? '' : value;
43787 fixPrecision : function(value)
43789 if(this.thousandsDelimiter) {
43791 r = new RegExp(",", "g");
43792 value = value.replace(r, "");
43795 var nan = isNaN(value);
43797 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43798 return nan ? '' : value;
43800 return parseFloat(value).toFixed(this.decimalPrecision);
43803 decimalPrecisionFcn : function(v)
43805 return Math.floor(v);
43808 validateValue : function(value)
43810 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43814 var num = this.parseValue(value);
43817 this.markInvalid(String.format(this.nanText, value));
43821 if(num < this.minValue){
43822 this.markInvalid(String.format(this.minText, this.minValue));
43826 if(num > this.maxValue){
43827 this.markInvalid(String.format(this.maxText, this.maxValue));
43834 validate : function()
43836 if(this.disabled || this.allowBlank){
43841 var currency = this.getCurrency();
43843 if(this.validateValue(this.getRawValue()) && currency.length){
43848 this.markInvalid();
43852 getName: function()
43857 beforeBlur : function()
43863 var v = this.parseValue(this.getRawValue());
43870 onBlur : function()
43874 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43875 //this.el.removeClass(this.focusClass);
43878 this.hasFocus = false;
43880 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43884 var v = this.getValue();
43886 if(String(v) !== String(this.startValue)){
43887 this.fireEvent('change', this, v, this.startValue);
43890 this.fireEvent("blur", this);
43893 inputEl : function()
43895 return this.el.select('.roo-money-amount-input', true).first();
43898 currencyEl : function()
43900 return this.el.select('.roo-money-currency-input', true).first();
43903 hiddenEl : function()
43905 return this.el.select('input.hidden-number-input',true).first();
43909 * @class Roo.bootstrap.BezierSignature
43910 * @extends Roo.bootstrap.Component
43911 * Bootstrap BezierSignature class
43912 * This script refer to:
43913 * Title: Signature Pad
43915 * Availability: https://github.com/szimek/signature_pad
43918 * Create a new BezierSignature
43919 * @param {Object} config The config object
43922 Roo.bootstrap.BezierSignature = function(config){
43923 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43929 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43936 mouse_btn_down: true,
43939 * @cfg {int} canvas height
43941 canvas_height: '200px',
43944 * @cfg {float|function} Radius of a single dot.
43949 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43954 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43959 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43964 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43969 * @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.
43971 bg_color: 'rgba(0, 0, 0, 0)',
43974 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43976 dot_color: 'black',
43979 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43981 velocity_filter_weight: 0.7,
43984 * @cfg {function} Callback when stroke begin.
43989 * @cfg {function} Callback when stroke end.
43993 getAutoCreate : function()
43995 var cls = 'roo-signature column';
43998 cls += ' ' + this.cls;
44008 for(var i = 0; i < col_sizes.length; i++) {
44009 if(this[col_sizes[i]]) {
44010 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44020 cls: 'roo-signature-body',
44024 cls: 'roo-signature-body-canvas',
44025 height: this.canvas_height,
44026 width: this.canvas_width
44033 style: 'display: none'
44041 initEvents: function()
44043 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44045 var canvas = this.canvasEl();
44047 // mouse && touch event swapping...
44048 canvas.dom.style.touchAction = 'none';
44049 canvas.dom.style.msTouchAction = 'none';
44051 this.mouse_btn_down = false;
44052 canvas.on('mousedown', this._handleMouseDown, this);
44053 canvas.on('mousemove', this._handleMouseMove, this);
44054 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44056 if (window.PointerEvent) {
44057 canvas.on('pointerdown', this._handleMouseDown, this);
44058 canvas.on('pointermove', this._handleMouseMove, this);
44059 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44062 if ('ontouchstart' in window) {
44063 canvas.on('touchstart', this._handleTouchStart, this);
44064 canvas.on('touchmove', this._handleTouchMove, this);
44065 canvas.on('touchend', this._handleTouchEnd, this);
44068 Roo.EventManager.onWindowResize(this.resize, this, true);
44070 // file input event
44071 this.fileEl().on('change', this.uploadImage, this);
44078 resize: function(){
44080 var canvas = this.canvasEl().dom;
44081 var ctx = this.canvasElCtx();
44082 var img_data = false;
44084 if(canvas.width > 0) {
44085 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44087 // setting canvas width will clean img data
44090 var style = window.getComputedStyle ?
44091 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44093 var padding_left = parseInt(style.paddingLeft) || 0;
44094 var padding_right = parseInt(style.paddingRight) || 0;
44096 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44099 ctx.putImageData(img_data, 0, 0);
44103 _handleMouseDown: function(e)
44105 if (e.browserEvent.which === 1) {
44106 this.mouse_btn_down = true;
44107 this.strokeBegin(e);
44111 _handleMouseMove: function (e)
44113 if (this.mouse_btn_down) {
44114 this.strokeMoveUpdate(e);
44118 _handleMouseUp: function (e)
44120 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44121 this.mouse_btn_down = false;
44126 _handleTouchStart: function (e) {
44128 e.preventDefault();
44129 if (e.browserEvent.targetTouches.length === 1) {
44130 // var touch = e.browserEvent.changedTouches[0];
44131 // this.strokeBegin(touch);
44133 this.strokeBegin(e); // assume e catching the correct xy...
44137 _handleTouchMove: function (e) {
44138 e.preventDefault();
44139 // var touch = event.targetTouches[0];
44140 // _this._strokeMoveUpdate(touch);
44141 this.strokeMoveUpdate(e);
44144 _handleTouchEnd: function (e) {
44145 var wasCanvasTouched = e.target === this.canvasEl().dom;
44146 if (wasCanvasTouched) {
44147 e.preventDefault();
44148 // var touch = event.changedTouches[0];
44149 // _this._strokeEnd(touch);
44154 reset: function () {
44155 this._lastPoints = [];
44156 this._lastVelocity = 0;
44157 this._lastWidth = (this.min_width + this.max_width) / 2;
44158 this.canvasElCtx().fillStyle = this.dot_color;
44161 strokeMoveUpdate: function(e)
44163 this.strokeUpdate(e);
44165 if (this.throttle) {
44166 this.throttleStroke(this.strokeUpdate, this.throttle);
44169 this.strokeUpdate(e);
44173 strokeBegin: function(e)
44175 var newPointGroup = {
44176 color: this.dot_color,
44180 if (typeof this.onBegin === 'function') {
44184 this.curve_data.push(newPointGroup);
44186 this.strokeUpdate(e);
44189 strokeUpdate: function(e)
44191 var rect = this.canvasEl().dom.getBoundingClientRect();
44192 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44193 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44194 var lastPoints = lastPointGroup.points;
44195 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44196 var isLastPointTooClose = lastPoint
44197 ? point.distanceTo(lastPoint) <= this.min_distance
44199 var color = lastPointGroup.color;
44200 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44201 var curve = this.addPoint(point);
44203 this.drawDot({color: color, point: point});
44206 this.drawCurve({color: color, curve: curve});
44216 strokeEnd: function(e)
44218 this.strokeUpdate(e);
44219 if (typeof this.onEnd === 'function') {
44224 addPoint: function (point) {
44225 var _lastPoints = this._lastPoints;
44226 _lastPoints.push(point);
44227 if (_lastPoints.length > 2) {
44228 if (_lastPoints.length === 3) {
44229 _lastPoints.unshift(_lastPoints[0]);
44231 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44232 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44233 _lastPoints.shift();
44239 calculateCurveWidths: function (startPoint, endPoint) {
44240 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44241 (1 - this.velocity_filter_weight) * this._lastVelocity;
44243 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44246 start: this._lastWidth
44249 this._lastVelocity = velocity;
44250 this._lastWidth = newWidth;
44254 drawDot: function (_a) {
44255 var color = _a.color, point = _a.point;
44256 var ctx = this.canvasElCtx();
44257 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44259 this.drawCurveSegment(point.x, point.y, width);
44261 ctx.fillStyle = color;
44265 drawCurve: function (_a) {
44266 var color = _a.color, curve = _a.curve;
44267 var ctx = this.canvasElCtx();
44268 var widthDelta = curve.endWidth - curve.startWidth;
44269 var drawSteps = Math.floor(curve.length()) * 2;
44271 ctx.fillStyle = color;
44272 for (var i = 0; i < drawSteps; i += 1) {
44273 var t = i / drawSteps;
44279 var x = uuu * curve.startPoint.x;
44280 x += 3 * uu * t * curve.control1.x;
44281 x += 3 * u * tt * curve.control2.x;
44282 x += ttt * curve.endPoint.x;
44283 var y = uuu * curve.startPoint.y;
44284 y += 3 * uu * t * curve.control1.y;
44285 y += 3 * u * tt * curve.control2.y;
44286 y += ttt * curve.endPoint.y;
44287 var width = curve.startWidth + ttt * widthDelta;
44288 this.drawCurveSegment(x, y, width);
44294 drawCurveSegment: function (x, y, width) {
44295 var ctx = this.canvasElCtx();
44297 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44298 this.is_empty = false;
44303 var ctx = this.canvasElCtx();
44304 var canvas = this.canvasEl().dom;
44305 ctx.fillStyle = this.bg_color;
44306 ctx.clearRect(0, 0, canvas.width, canvas.height);
44307 ctx.fillRect(0, 0, canvas.width, canvas.height);
44308 this.curve_data = [];
44310 this.is_empty = true;
44315 return this.el.select('input',true).first();
44318 canvasEl: function()
44320 return this.el.select('canvas',true).first();
44323 canvasElCtx: function()
44325 return this.el.select('canvas',true).first().dom.getContext('2d');
44328 getImage: function(type)
44330 if(this.is_empty) {
44335 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44338 drawFromImage: function(img_src)
44340 var img = new Image();
44342 img.onload = function(){
44343 this.canvasElCtx().drawImage(img, 0, 0);
44348 this.is_empty = false;
44351 selectImage: function()
44353 this.fileEl().dom.click();
44356 uploadImage: function(e)
44358 var reader = new FileReader();
44360 reader.onload = function(e){
44361 var img = new Image();
44362 img.onload = function(){
44364 this.canvasElCtx().drawImage(img, 0, 0);
44366 img.src = e.target.result;
44369 reader.readAsDataURL(e.target.files[0]);
44372 // Bezier Point Constructor
44373 Point: (function () {
44374 function Point(x, y, time) {
44377 this.time = time || Date.now();
44379 Point.prototype.distanceTo = function (start) {
44380 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44382 Point.prototype.equals = function (other) {
44383 return this.x === other.x && this.y === other.y && this.time === other.time;
44385 Point.prototype.velocityFrom = function (start) {
44386 return this.time !== start.time
44387 ? this.distanceTo(start) / (this.time - start.time)
44394 // Bezier Constructor
44395 Bezier: (function () {
44396 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44397 this.startPoint = startPoint;
44398 this.control2 = control2;
44399 this.control1 = control1;
44400 this.endPoint = endPoint;
44401 this.startWidth = startWidth;
44402 this.endWidth = endWidth;
44404 Bezier.fromPoints = function (points, widths, scope) {
44405 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44406 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44407 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44409 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44410 var dx1 = s1.x - s2.x;
44411 var dy1 = s1.y - s2.y;
44412 var dx2 = s2.x - s3.x;
44413 var dy2 = s2.y - s3.y;
44414 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44415 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44416 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44417 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44418 var dxm = m1.x - m2.x;
44419 var dym = m1.y - m2.y;
44420 var k = l2 / (l1 + l2);
44421 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44422 var tx = s2.x - cm.x;
44423 var ty = s2.y - cm.y;
44425 c1: new scope.Point(m1.x + tx, m1.y + ty),
44426 c2: new scope.Point(m2.x + tx, m2.y + ty)
44429 Bezier.prototype.length = function () {
44434 for (var i = 0; i <= steps; i += 1) {
44436 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44437 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44439 var xdiff = cx - px;
44440 var ydiff = cy - py;
44441 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44448 Bezier.prototype.point = function (t, start, c1, c2, end) {
44449 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44450 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44451 + (3.0 * c2 * (1.0 - t) * t * t)
44452 + (end * t * t * t);
44457 throttleStroke: function(fn, wait) {
44458 if (wait === void 0) { wait = 250; }
44460 var timeout = null;
44464 var later = function () {
44465 previous = Date.now();
44467 result = fn.apply(storedContext, storedArgs);
44469 storedContext = null;
44473 return function wrapper() {
44475 for (var _i = 0; _i < arguments.length; _i++) {
44476 args[_i] = arguments[_i];
44478 var now = Date.now();
44479 var remaining = wait - (now - previous);
44480 storedContext = this;
44482 if (remaining <= 0 || remaining > wait) {
44484 clearTimeout(timeout);
44488 result = fn.apply(storedContext, storedArgs);
44490 storedContext = null;
44494 else if (!timeout) {
44495 timeout = window.setTimeout(later, remaining);